1
This commit is contained in:
parent
d08ccdff7e
commit
9546a0b4f3
133
README.md
133
README.md
@ -1,8 +1,8 @@
|
|||||||
# 低代码的服务器端应用框架 基于 [ssgo/s](https://github.com/ssgo/s)
|
# 低代码的服务器端应用框架 基于 [ssgo/s](https://github.com/ssgo/s)
|
||||||
|
|
||||||
快速创建一个web服务,提供 http、https、http2、h2c、websocket 服务
|
快速创建一个web服务,提供 http、https、http2、h2c、websocket 服务
|
||||||
支持作为静态文件服务器
|
支持作为静态文件服务器
|
||||||
支持 rewrite 和 proxy反向代理(可以代理到discover应用或其他http服务器)
|
支持 rewrite 和 proxy反向代理(可以代理到discover应用或其他http服务器)
|
||||||
支持服务发现 [ssgo/discover](https://github.com/ssgo/discover) 快速构建一个服务网络
|
支持服务发现 [ssgo/discover](https://github.com/ssgo/discover) 快速构建一个服务网络
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
@ -48,10 +48,8 @@ function main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这种模式下服务代码会脱离主线程,使用对象池实现高并发
|
这种模式下服务代码会脱离主线程,使用对象池实现高并发
|
||||||
|
如果不配置对象池参数则不约束虚拟机数量上限,可以达到最佳性能但是对CPU和内存有一定挑战
|
||||||
如果不配置对象池参数则不约束虚拟机数量上限,可以达到最佳性能但是对CPU和内存有一定挑战
|
|
||||||
|
|
||||||
设置较小的max可以有效保护CPU和内存资源但是设置过小将无法发挥服务器的性能
|
设置较小的max可以有效保护CPU和内存资源但是设置过小将无法发挥服务器的性能
|
||||||
|
|
||||||
## 注册到服务发现
|
## 注册到服务发现
|
||||||
@ -85,20 +83,15 @@ s.register({ path: '/getUserInfo' }, ({ caller }) => {
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
authLevel 不设置则不进行校验,如果获得 authLevel 2 则允许访问所有 2及以下级别的接口
|
authLevel 不设置则不进行校验,如果获得 authLevel 2 则允许访问所有 2及以下级别的接口
|
||||||
|
如果 user 服务不配置 listen 默认使用 h2c 协议随机端口
|
||||||
如果 user 服务不配置 listen 默认使用 h2c 协议随机端口
|
|
||||||
|
|
||||||
如果不使用 h2c 协议,调用方配置 calls 时需指定 'http:aaaaaaaa'
|
如果不使用 h2c 协议,调用方配置 calls 时需指定 'http:aaaaaaaa'
|
||||||
|
|
||||||
## Session
|
## Session
|
||||||
|
|
||||||
服务默认启用 session 和 device 功能,如果不希望使用可以在配置中设置 sessionKey 或 deviceKey 为空
|
服务默认启用 session 和 device 功能,如果不希望使用可以在配置中设置 sessionKey 或 deviceKey 为空
|
||||||
|
如果配置了 sessionProvider,session 将会存储在 redis 中,否则存储在内存中
|
||||||
如果配置了 sessionProvider,session 将会存储在 redis 中,否则存储在内存中
|
sessionID和deviceID 同时支持HTTP头和Cookie两种传输方式,HTTP头优先,如果客户端没有传递则服务器会自动分配
|
||||||
|
|
||||||
sessionID和deviceID 同时支持HTTP头和Cookie两种传输方式,HTTP头优先,如果客户端没有传递则服务器会自动分配
|
|
||||||
|
|
||||||
如需使用 session 只需要在接口中直接获取即可
|
如需使用 session 只需要在接口中直接获取即可
|
||||||
|
|
||||||
#### 下面是一个使用 session 并且使用参数有效性验证和限流器的例子
|
#### 下面是一个使用 session 并且使用参数有效性验证和限流器的例子
|
||||||
@ -138,16 +131,11 @@ function main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
session对象自动注入,无需任何其他操作。修改session后需要使用 session.save 来保存
|
session对象自动注入,无需任何其他操作。修改session后需要使用 session.save 来保存
|
||||||
|
调用 session.setAuthLevel 可以设置用户权限,当接口注册的 authLevel 大于0时可以基于 session 中的设置进行访问控制
|
||||||
调用 session.setAuthLevel 可以设置用户权限,当接口注册的 authLevel 大于0时可以基于 session 中的设置进行访问控制
|
配置了 userIdKey 后,会自动将 session 的用户ID记录在访问日志中,方便对用户的访问进行分析和统计
|
||||||
|
示例中创建了一个每个IP每秒允许10次请求的限流器并且在接口中使用了这个限流器
|
||||||
配置了 userIdKey 后,会自动将 session 的用户ID记录在访问日志中,方便对用户的访问进行分析和统计
|
login接口配置了 id 和 name 两个参数的有效性验证规则
|
||||||
|
|
||||||
示例中创建了一个每个IP每秒允许10次请求的限流器并且在接口中使用了这个限流器
|
|
||||||
|
|
||||||
login接口配置了 id 和 name 两个参数的有效性验证规则
|
|
||||||
|
|
||||||
参数有效性验证配置可以支持以下类型:
|
参数有效性验证配置可以支持以下类型:
|
||||||
|
|
||||||
- value为string或RegExp对象时进行正则表达式校验
|
- value为string或RegExp对象时进行正则表达式校验
|
||||||
@ -169,8 +157,6 @@ ssl:
|
|||||||
yourdomain.com:
|
yourdomain.com:
|
||||||
certfile: /path/yourdomain.pem
|
certfile: /path/yourdomain.pem
|
||||||
keyfile: /path/yourdomain.pem
|
keyfile: /path/yourdomain.pem
|
||||||
static:
|
|
||||||
yourdomain.com: /path/www
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2、在环境配置文件 env.yml 或 env.json 中配置
|
2、在环境配置文件 env.yml 或 env.json 中配置
|
||||||
@ -188,6 +174,40 @@ docker run -e SERVICE_LISTEN=8080:8443
|
|||||||
|
|
||||||
#### 所有配置方式的优先级为 s.config > 环境变量 > env.yml > service.yml
|
#### 所有配置方式的优先级为 s.config > 环境变量 > env.yml > service.yml
|
||||||
|
|
||||||
|
## 静态文件
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
service:
|
||||||
|
static:
|
||||||
|
yourdomain.com: /path/www
|
||||||
|
yourdomain.com:8080: /path/www8080
|
||||||
|
yourdomain.com:80/abc: /path/abc
|
||||||
|
/def: /path/def
|
||||||
|
```
|
||||||
|
|
||||||
|
可以根据域名和路径配置静态文件
|
||||||
|
可以使用 file 模块将文件加载到内存中加速访问
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import file from "apigo.cc/gojs/file"
|
||||||
|
|
||||||
|
file.cache('/path/www', true)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 反向代理和Rewrite
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
service:
|
||||||
|
proxy:
|
||||||
|
yourdomain.com: serverA
|
||||||
|
/abc: http://HOST:PORT/PATH
|
||||||
|
yourdomain.com/def/(.*): http://127.0.0.1:8080/$1
|
||||||
|
rewrite:
|
||||||
|
yourdomain.com/001/(.*): /path/001/$1
|
||||||
|
yourdomain.com/002/(.*): http://127.0.0.1:8080/$1
|
||||||
|
http://yourdomain.com(.*): https://yourdomain.com$1
|
||||||
|
```
|
||||||
|
|
||||||
## websocket
|
## websocket
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -212,5 +232,60 @@ function main() {
|
|||||||
|
|
||||||
注册接口时将 method 指定为 WS 即可创建 websocket 服务,配置 onMessage 来异步处理消息
|
注册接口时将 method 指定为 WS 即可创建 websocket 服务,配置 onMessage 来异步处理消息
|
||||||
|
|
||||||
|
## 后台任务
|
||||||
|
|
||||||
|
#### taskA.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import s from "apigo.cc/gojs/service"
|
||||||
|
|
||||||
|
// function onStart() {
|
||||||
|
// // TODO 任务启动时执行
|
||||||
|
// }
|
||||||
|
|
||||||
|
function onRun() {
|
||||||
|
// TODO 在指定间隔时间到达时被调用,根据需要对资源进行处理
|
||||||
|
if ( s.dataCount('websocketClients') > 0 ) {
|
||||||
|
let conns = s.dataFetch('websocketClients')
|
||||||
|
for ( let conn of conns ) {
|
||||||
|
conn.write('Hello, World!')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStop() {
|
||||||
|
// TODO 服务结束时被调用,用来收尾或释放资源
|
||||||
|
s.dataRemove('websocketClients')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### main.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import s from "apigo.cc/gojs/service"
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
s.task('task.js', 1000) // 每秒执行一次
|
||||||
|
s.register({method: 'WS', path: '/ws'}, ({ client }) => {
|
||||||
|
// 将连接放到资源池中供后台任务使用
|
||||||
|
s.dataSet('websocketClients', client.id, client)
|
||||||
|
})
|
||||||
|
s.start()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
task 必须在单独的js文件中定义
|
||||||
|
每个 task 都会运行在单独的vm中
|
||||||
|
定义 task 必须在服务启动(s.start)之前
|
||||||
|
服务停止(s.stop)所有任务会被停止
|
||||||
|
|
||||||
|
#### 任务队列
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
s.listPush('taskA', {})
|
||||||
|
s.listPop('taskA', {})
|
||||||
|
```
|
||||||
|
|
||||||
|
可以使用 list 相关操作实现基于队列的后台任务处理
|
||||||
|
|
||||||
## 完整的API参考 [service.ts](https://apigo.cc/gojs/service/src/branch/main/service.ts)
|
## 完整的API参考 [service.ts](https://apigo.cc/gojs/service/src/branch/main/service.ts)
|
||||||
|
@ -80,7 +80,7 @@ func (cl *Caller) makeHeaderArray(in map[string]any) []string {
|
|||||||
func parseAppPath(appURL string) (string, string) {
|
func parseAppPath(appURL string) (string, string) {
|
||||||
arr := strings.SplitN(appURL, "/", 2)
|
arr := strings.SplitN(appURL, "/", 2)
|
||||||
if len(arr) == 2 {
|
if len(arr) == 2 {
|
||||||
return arr[0], arr[1]
|
return arr[0], "/" + arr[1]
|
||||||
}
|
}
|
||||||
return appURL, "/"
|
return appURL, "/"
|
||||||
}
|
}
|
||||||
|
391
gateway.go
391
gateway.go
@ -2,6 +2,7 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -13,25 +14,21 @@ import (
|
|||||||
"github.com/ssgo/u"
|
"github.com/ssgo/u"
|
||||||
)
|
)
|
||||||
|
|
||||||
type regexProxiesInfo struct {
|
type regexRedirectInfo struct {
|
||||||
Value string
|
|
||||||
Regex regexp.Regexp
|
|
||||||
}
|
|
||||||
|
|
||||||
type regexRewriteInfo struct {
|
|
||||||
To string
|
To string
|
||||||
Regex regexp.Regexp
|
Regex regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
var _proxies = map[string]string{}
|
var _proxies = map[string]string{}
|
||||||
var _proxiesLock = sync.RWMutex{}
|
var _proxiesLock = sync.RWMutex{}
|
||||||
var _regexProxies = map[string]*regexProxiesInfo{}
|
var _regexProxies = map[string]*regexRedirectInfo{}
|
||||||
var _regexRewrites = map[string]*regexRewriteInfo{}
|
var _rewrites = map[string]string{}
|
||||||
|
var _regexRewrites = map[string]*regexRedirectInfo{}
|
||||||
var _rewritesLock = sync.RWMutex{}
|
var _rewritesLock = sync.RWMutex{}
|
||||||
var _statics = map[string]string{}
|
var _statics = map[string]string{}
|
||||||
var _staticsLock = sync.RWMutex{}
|
var _staticsLock = sync.RWMutex{}
|
||||||
|
|
||||||
func updateStatic(in map[string]string) bool {
|
func UpdateStatic(in map[string]string) bool {
|
||||||
updated := false
|
updated := false
|
||||||
for k, v := range in {
|
for k, v := range in {
|
||||||
_staticsLock.RLock()
|
_staticsLock.RLock()
|
||||||
@ -52,13 +49,16 @@ func updateStatic(in map[string]string) bool {
|
|||||||
if a[0] == "*" {
|
if a[0] == "*" {
|
||||||
a[0] = ""
|
a[0] = ""
|
||||||
}
|
}
|
||||||
|
if a[1] == "" {
|
||||||
|
a[1] = "/"
|
||||||
|
}
|
||||||
s.StaticByHost(a[1], v, a[0])
|
s.StaticByHost(a[1], v, a[0])
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateProxy(in map[string]string) bool {
|
func UpdateProxy(in map[string]string) bool {
|
||||||
updated := false
|
updated := false
|
||||||
//fmt.Println("####000")
|
//fmt.Println("####000")
|
||||||
|
|
||||||
@ -74,23 +74,22 @@ func updateProxy(in map[string]string) bool {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
////fmt.Println("####333", k, v)
|
////fmt.Println("####333", k, v)
|
||||||
if v2 != nil && v == v2.Value {
|
if v2 != nil && v == v2.To {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
////fmt.Println("####444", k, v)
|
////fmt.Println("####444", k, v)
|
||||||
|
if strings.Contains(k, "(") {
|
||||||
if strings.Contains(v, "(") {
|
|
||||||
// for regexp
|
// for regexp
|
||||||
////fmt.Println("####555", k, v)
|
////fmt.Println("####555", k, v)
|
||||||
matcher, err := regexp.Compile("^" + v + "$")
|
matcher, err := regexp.Compile("^" + k + "$")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.ServerLogger.Error("proxy regexp compile failed", "key", k, "value", v)
|
s.ServerLogger.Error("proxy regexp compile failed", "key", k, "value", v)
|
||||||
//log.Print("Proxy Error Compile ", err)
|
//log.Print("Proxy Error Compile ", err)
|
||||||
} else {
|
} else {
|
||||||
s.ServerLogger.Info(u.StringIf(v2 != nil, "update regexp proxy set", "new regexp proxy set"), "key", k, "value", v)
|
s.ServerLogger.Info(u.StringIf(v2 != nil, "update regexp proxy set", "new regexp proxy set"), "key", k, "value", v)
|
||||||
_proxiesLock.Lock()
|
_proxiesLock.Lock()
|
||||||
_regexProxies[k] = ®exProxiesInfo{
|
_regexProxies[k] = ®exRedirectInfo{
|
||||||
Value: v,
|
To: v,
|
||||||
Regex: *matcher,
|
Regex: *matcher,
|
||||||
}
|
}
|
||||||
_proxiesLock.Unlock()
|
_proxiesLock.Unlock()
|
||||||
@ -117,11 +116,11 @@ func updateProxy(in map[string]string) bool {
|
|||||||
} else {
|
} else {
|
||||||
callConfig = (time.Duration(s.Config.ReadHeaderTimeout) * time.Millisecond).String()
|
callConfig = (time.Duration(s.Config.ReadHeaderTimeout) * time.Millisecond).String()
|
||||||
}
|
}
|
||||||
// if redisPool != nil {
|
if discover.Config.Registry != "" {
|
||||||
if discover.AddExternalApp(v, callConfig) {
|
if discover.AddExternalApp(v, callConfig) {
|
||||||
updated = true
|
updated = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
@ -134,28 +133,39 @@ func updateProxy(in map[string]string) bool {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateRewrite(in map[string]string) bool {
|
func UpdateRewrite(in map[string]string) bool {
|
||||||
updated := false
|
updated := false
|
||||||
for k, v := range in {
|
for k, v := range in {
|
||||||
_rewritesLock.RLock()
|
_rewritesLock.RLock()
|
||||||
|
v1 := _rewrites[k]
|
||||||
v2 := _regexRewrites[k]
|
v2 := _regexRewrites[k]
|
||||||
_rewritesLock.RUnlock()
|
_rewritesLock.RUnlock()
|
||||||
|
|
||||||
// skip same
|
// skip same
|
||||||
|
if v == v1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if v2 != nil && v == v2.To {
|
if v2 != nil && v == v2.To {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
matcher, err := regexp.Compile("^" + k + "$")
|
if strings.Contains(k, "(") {
|
||||||
if err != nil {
|
matcher, err := regexp.Compile("^" + k + "$")
|
||||||
s.ServerLogger.Error("rewrite regexp compile failed", "key", k, "value", v)
|
if err != nil {
|
||||||
} else {
|
s.ServerLogger.Error("rewrite regexp compile failed", "key", k, "value", v)
|
||||||
s.ServerLogger.Info(u.StringIf(v2 != nil, "update regexp rewrite set", "new regexp rewrite set"), "key", k, "value", v)
|
} else {
|
||||||
_rewritesLock.Lock()
|
s.ServerLogger.Info(u.StringIf(v2 != nil, "update regexp rewrite set", "new regexp rewrite set"), "key", k, "value", v)
|
||||||
_regexRewrites[k] = ®exRewriteInfo{
|
_rewritesLock.Lock()
|
||||||
To: v,
|
_regexRewrites[k] = ®exRedirectInfo{
|
||||||
Regex: *matcher,
|
To: v,
|
||||||
|
Regex: *matcher,
|
||||||
|
}
|
||||||
|
_rewritesLock.Unlock()
|
||||||
|
updated = true
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
_rewritesLock.Lock()
|
||||||
|
_rewrites[k] = v
|
||||||
_rewritesLock.Unlock()
|
_rewritesLock.Unlock()
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
@ -163,50 +173,66 @@ func updateRewrite(in map[string]string) bool {
|
|||||||
return updated
|
return updated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 测试各种情况下的 matchRedirect
|
||||||
func rewrite(request *s.Request) (toPath string, rewrite bool) {
|
func rewrite(request *s.Request) (toPath string, rewrite bool) {
|
||||||
list2 := map[string]*regexRewriteInfo{}
|
if toApp, toPath, ok := matchRedirect(request, &_rewrites, &_regexRewrites, &_rewritesLock); ok {
|
||||||
_rewritesLock.RLock()
|
return toApp + toPath, true
|
||||||
for k, v := range _regexRewrites {
|
|
||||||
list2[k] = v
|
|
||||||
}
|
}
|
||||||
_rewritesLock.RUnlock()
|
return
|
||||||
|
|
||||||
if len(list2) > 0 {
|
|
||||||
requestUrl := fmt.Sprint(request.Header.Get("X-Scheme"), "://", request.Host, request.RequestURI)
|
|
||||||
requestUrlWithoutScheme := fmt.Sprint(request.Host, request.RequestURI)
|
|
||||||
|
|
||||||
for _, rr := range list2 {
|
|
||||||
finds := rr.Regex.FindAllStringSubmatch(requestUrl, 20)
|
|
||||||
if len(finds) == 0 {
|
|
||||||
finds = rr.Regex.FindAllStringSubmatch(requestUrlWithoutScheme, 20)
|
|
||||||
}
|
|
||||||
if len(finds) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
to := rr.To
|
|
||||||
if len(finds[0]) > 1 {
|
|
||||||
for i := 1; i < len(finds[0]); i++ {
|
|
||||||
varName := fmt.Sprintf("$%d", i)
|
|
||||||
to = strings.ReplaceAll(to, varName, finds[0][i])
|
|
||||||
}
|
|
||||||
return to, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 不进行代理
|
|
||||||
return "", false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxy(request *s.Request) (authLevel int, toApp, toPath *string, headers map[string]string) {
|
func proxy(request *s.Request) (authLevel int, toApp, toPath *string, headers map[string]string) {
|
||||||
//fmt.Println("proxy", len(_proxies))
|
if toApp1, toPath1, ok := matchRedirect(request, &_proxies, &_regexProxies, &_proxiesLock); ok {
|
||||||
outHeaders := map[string]string{
|
outHeaders := map[string]string{
|
||||||
standard.DiscoverHeaderFromApp: "gateway",
|
standard.DiscoverHeaderFromApp: "gateway",
|
||||||
standard.DiscoverHeaderFromNode: s.GetServerAddr(),
|
standard.DiscoverHeaderFromNode: s.GetServerAddr(),
|
||||||
|
}
|
||||||
|
requestPath := request.RequestURI
|
||||||
|
if requestPath == "" {
|
||||||
|
requestPath = request.URL.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := strings.Index(requestPath, toPath1)
|
||||||
|
if pos > 0 {
|
||||||
|
outHeaders["Proxy-Path"] = requestPath[0:pos]
|
||||||
|
}
|
||||||
|
return 0, &toApp1, &toPath1, outHeaders
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearRewritesAndProxies() {
|
||||||
|
_staticsLock.Lock()
|
||||||
|
_statics = map[string]string{}
|
||||||
|
_staticsLock.Unlock()
|
||||||
|
_rewritesLock.Lock()
|
||||||
|
_rewrites = map[string]string{}
|
||||||
|
_regexRewrites = map[string]*regexRedirectInfo{}
|
||||||
|
_rewritesLock.Unlock()
|
||||||
|
_proxiesLock.Lock()
|
||||||
|
_proxies = map[string]string{}
|
||||||
|
_regexProxies = map[string]*regexRedirectInfo{}
|
||||||
|
_proxiesLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchRewrite(request *s.Request) (toApp, toPath string, ok bool) {
|
||||||
|
return matchRedirect(request, &_rewrites, &_regexRewrites, &_rewritesLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MatchProxy(request *s.Request) (toApp, toPath string, ok bool) {
|
||||||
|
return matchRedirect(request, &_proxies, &_regexProxies, &_proxiesLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchRedirect(request *s.Request, normalList *map[string]string, regexpList *map[string]*regexRedirectInfo, lock *sync.RWMutex) (toApp, toPath string, ok bool) {
|
||||||
|
(*lock).RLock()
|
||||||
|
n1 := len(*normalList)
|
||||||
|
n2 := len(*regexpList)
|
||||||
|
(*lock).RUnlock()
|
||||||
|
|
||||||
|
if n1 == 0 && n2 == 0 {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
scheme := u.StringIf(request.TLS == nil, "http", "https")
|
|
||||||
host1 := ""
|
host1 := ""
|
||||||
host2 := ""
|
host2 := ""
|
||||||
if strings.ContainsRune(request.Host, ':') {
|
if strings.ContainsRune(request.Host, ':') {
|
||||||
@ -215,98 +241,132 @@ func proxy(request *s.Request) (authLevel int, toApp, toPath *string, headers ma
|
|||||||
host2 = request.Host
|
host2 = request.Host
|
||||||
} else {
|
} else {
|
||||||
host1 = request.Host
|
host1 = request.Host
|
||||||
host2 = request.Host + ":" + u.StringIf(request.TLS == nil, "80", "443")
|
host2 = request.Host + ":" + u.StringIf(request.URL.Scheme == "https", "443", "80")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestPath := request.RequestURI
|
||||||
|
if requestPath == "" {
|
||||||
|
requestPath = request.URL.Path
|
||||||
|
}
|
||||||
pathMatchers := make([]string, 0)
|
pathMatchers := make([]string, 0)
|
||||||
pathMatchers = append(pathMatchers, fmt.Sprint(scheme, "://", host1, request.RequestURI))
|
pathMatchers = append(pathMatchers, fmt.Sprint(request.URL.Scheme, "://", host1, requestPath))
|
||||||
pathMatchers = append(pathMatchers, fmt.Sprint(scheme, "://", host2, request.RequestURI))
|
pathMatchers = append(pathMatchers, fmt.Sprint(request.URL.Scheme, "://", host2, requestPath))
|
||||||
pathMatchers = append(pathMatchers, fmt.Sprint(host1, request.RequestURI))
|
pathMatchers = append(pathMatchers, fmt.Sprint(host1, requestPath))
|
||||||
pathMatchers = append(pathMatchers, fmt.Sprint(host2, request.RequestURI))
|
pathMatchers = append(pathMatchers, fmt.Sprint(host2, requestPath))
|
||||||
pathMatchers = append(pathMatchers, request.RequestURI)
|
pathMatchers = append(pathMatchers, requestPath)
|
||||||
|
|
||||||
hostMatchers := make([]string, 0)
|
hostMatchers := make([]string, 0)
|
||||||
hostMatchers = append(hostMatchers, fmt.Sprint(scheme, "://", host1))
|
hostMatchers = append(hostMatchers, fmt.Sprint(request.URL.Scheme, "://", host1))
|
||||||
hostMatchers = append(hostMatchers, fmt.Sprint(scheme, "://", host2))
|
hostMatchers = append(hostMatchers, fmt.Sprint(request.URL.Scheme, "://", host2))
|
||||||
hostMatchers = append(hostMatchers, host1)
|
hostMatchers = append(hostMatchers, host1)
|
||||||
hostMatchers = append(hostMatchers, host2)
|
hostMatchers = append(hostMatchers, host2)
|
||||||
|
|
||||||
list := map[string]string{}
|
if n1 > 0 {
|
||||||
_proxiesLock.RLock()
|
list1 := map[string]string{}
|
||||||
for k, v := range _proxies {
|
(*lock).RLock()
|
||||||
list[k] = v
|
for k, v := range *normalList {
|
||||||
}
|
list1[k] = v
|
||||||
_proxiesLock.RUnlock()
|
|
||||||
for p, a := range list {
|
|
||||||
//fmt.Println("check proxy ", p, a)
|
|
||||||
matchPath := ""
|
|
||||||
matchPathArr := strings.SplitN(strings.ReplaceAll(p, "://", ""), "/", 2)
|
|
||||||
if len(matchPathArr) == 2 {
|
|
||||||
matchPath = "/" + matchPathArr[1]
|
|
||||||
}
|
}
|
||||||
|
(*lock).RUnlock()
|
||||||
if matchPath == "" {
|
for setKey, setValue := range list1 {
|
||||||
for _, m := range hostMatchers {
|
matchPath := ""
|
||||||
if m == p {
|
matchPathArr := strings.SplitN(strings.ReplaceAll(setKey, "://", ""), "/", 2)
|
||||||
//fmt.Println(" >>>>>>>>1", p, m, request.RequestURI)
|
if len(matchPathArr) == 2 {
|
||||||
return 0, fixAppName(a), &request.RequestURI, outHeaders
|
matchPath = "/" + matchPathArr[1]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for _, m := range pathMatchers {
|
toApp, toPath = splitAppAndPath(setValue)
|
||||||
if strings.HasPrefix(m, p) {
|
if matchPath == "" {
|
||||||
if strings.HasPrefix(request.RequestURI, matchPath) {
|
for _, matchStr := range hostMatchers {
|
||||||
p2 := request.RequestURI[len(matchPath):]
|
if matchStr == setKey {
|
||||||
if len(p2) == 0 || p2[0] != '/' {
|
// fmt.Println(" >>>>>>>>1", setKey, matchStr, requestPath)
|
||||||
p2 = "/" + p2
|
return toApp, path.Join(toPath, requestPath), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, matchStr := range pathMatchers {
|
||||||
|
// fmt.Println(" >>>>>>>> test", u.BCyan(matchStr), u.BMagenta(setKey), "|", strings.HasPrefix(matchStr, setKey))
|
||||||
|
if strings.HasPrefix(matchStr, setKey) {
|
||||||
|
if strings.HasPrefix(requestPath, matchPath) {
|
||||||
|
p2 := requestPath[len(matchPath):]
|
||||||
|
if len(p2) == 0 || p2[0] != '/' {
|
||||||
|
p2 = "/" + p2
|
||||||
|
}
|
||||||
|
// fmt.Println(" >>>>>>>>2", setKey, matchStr, p2)
|
||||||
|
return toApp, path.Join(toPath, p2), true
|
||||||
|
} else {
|
||||||
|
// fmt.Println(" >>>>>>>>3", setKey, matchStr, requestPath)
|
||||||
|
return toApp, path.Join(toPath, requestPath), true
|
||||||
}
|
}
|
||||||
//fmt.Println(" >>>>>>>>2", p, m, p2)
|
|
||||||
return 0, fixAppName(a), &p2, outHeaders
|
|
||||||
} else {
|
|
||||||
//fmt.Println(" >>>>>>>>3", p, m, request.RequestURI)
|
|
||||||
return 0, fixAppName(a), &request.RequestURI, outHeaders
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模糊匹配
|
if n2 > 0 {
|
||||||
list2 := map[string]*regexProxiesInfo{}
|
// 模糊匹配
|
||||||
_proxiesLock.RLock()
|
list2 := map[string]*regexRedirectInfo{}
|
||||||
for k, v := range _regexProxies {
|
(*lock).RLock()
|
||||||
list2[k] = v
|
for k, v := range *regexpList {
|
||||||
}
|
list2[k] = v
|
||||||
_proxiesLock.RUnlock()
|
}
|
||||||
|
(*lock).RUnlock()
|
||||||
|
|
||||||
if len(list2) > 0 {
|
// requestUrl := request.Host + requestPath
|
||||||
requestUrl := request.Host + request.RequestURI
|
for setKey, setInfo := range list2 {
|
||||||
for _, rp := range list2 {
|
matchPath := ""
|
||||||
//fmt.Println("check regexp proxy ", rp.Regex, rp.Value)
|
matchPathArr := strings.SplitN(strings.ReplaceAll(setKey, "://", ""), "/", 2)
|
||||||
finds := rp.Regex.FindAllStringSubmatch(requestUrl, 20)
|
if len(matchPathArr) == 2 {
|
||||||
if len(finds) > 0 && len(finds[0]) > 2 {
|
matchPath = "/" + matchPathArr[1]
|
||||||
//fmt.Println(" >>>>>>>>2", request.RequestURI, finds[0][2])
|
|
||||||
pos := strings.Index(request.RequestURI, finds[0][2])
|
|
||||||
if pos > 0 {
|
|
||||||
outHeaders["Proxy-Path"] = request.RequestURI[0:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(finds[0][1], "://") && strings.ContainsRune(finds[0][1], ':') {
|
|
||||||
callConfig := ""
|
|
||||||
if strings.ContainsRune(finds[0][1], ':') {
|
|
||||||
// support call config in proxy value
|
|
||||||
a := strings.SplitN(finds[0][1], ":", 2)
|
|
||||||
finds[0][1] = a[0]
|
|
||||||
callConfig = a[1]
|
|
||||||
} else {
|
|
||||||
callConfig = (time.Duration(s.Config.ReadHeaderTimeout) * time.Millisecond).String()
|
|
||||||
}
|
|
||||||
// if redisPool != nil {
|
|
||||||
discover.AddExternalApp(finds[0][1], callConfig)
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
return 0, &finds[0][1], &finds[0][2], outHeaders
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fmt.Println(" >>>>>>>> matchPath", setKey, matchPath, u.JsonP(matchPathArr), 111)
|
||||||
|
var matchList []string
|
||||||
|
if matchPath == "" {
|
||||||
|
matchList = hostMatchers
|
||||||
|
} else {
|
||||||
|
matchList = pathMatchers
|
||||||
|
}
|
||||||
|
for _, matchStr := range matchList {
|
||||||
|
finds := setInfo.Regex.FindStringSubmatch(matchStr)
|
||||||
|
if len(finds) > 0 {
|
||||||
|
matchResult := setInfo.To
|
||||||
|
for i := 1; i < len(finds); i++ {
|
||||||
|
matchResult = strings.ReplaceAll(matchResult, fmt.Sprintf("$%d", i), finds[i])
|
||||||
|
}
|
||||||
|
// fmt.Println(" >>>>>>>> test", u.BCyan(matchStr), u.Cyan(matchPath), u.BMagenta(setKey), "|", matchResult, fixAppName(matchResult), "...")
|
||||||
|
toApp, toPath := splitAppAndPath(matchResult)
|
||||||
|
return toApp, toPath, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// //fmt.Println("check regexp proxy ", rp.Regex, rp.Value)
|
||||||
|
// // finds := setInfo.Regex.FindAllStringSubmatch(requestUrl, 20)
|
||||||
|
// fmt.Println(" >>>>>>>> test", u.BCyan(matchStr), u.BMagenta(setKey), "|", strings.HasPrefix(matchStr, setKey))
|
||||||
|
// if len(finds) > 0 && len(finds[0]) > 2 {
|
||||||
|
// //fmt.Println(" >>>>>>>>2", requestPath, finds[0][2])
|
||||||
|
// // pos := strings.Index(requestPath, finds[0][2])
|
||||||
|
// // if pos > 0 {
|
||||||
|
// // outHeaders["Proxy-Path"] = requestPath[0:pos]
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// if !strings.Contains(finds[0][1], "://") && strings.ContainsRune(finds[0][1], ':') {
|
||||||
|
// callConfig := ""
|
||||||
|
// if strings.ContainsRune(finds[0][1], ':') {
|
||||||
|
// // support call config in proxy value
|
||||||
|
// a := strings.SplitN(finds[0][1], ":", 2)
|
||||||
|
// finds[0][1] = a[0]
|
||||||
|
// callConfig = a[1]
|
||||||
|
// } else {
|
||||||
|
// callConfig = (time.Duration(s.Config.ReadHeaderTimeout) * time.Millisecond).String()
|
||||||
|
// }
|
||||||
|
// if discover.Config.Registry != "" {
|
||||||
|
// discover.AddExternalApp(finds[0][1], callConfig)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return finds[0][1], finds[0][2], true
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,11 +374,52 @@ func proxy(request *s.Request) (authLevel int, toApp, toPath *string, headers ma
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixAppName(appName string) *string {
|
// func fixAppName(appName string) string {
|
||||||
if !strings.Contains(appName, "://") && strings.ContainsRune(appName, ':') {
|
// if !strings.Contains(appName, "://") && strings.ContainsRune(appName, ':') {
|
||||||
a := strings.SplitN(appName, ":", 2)
|
// a := strings.SplitN(appName, "/", 2)
|
||||||
return &a[0]
|
// return a[0]
|
||||||
|
// } else {
|
||||||
|
// return appName
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func splitAppAndPath(to string) (toApp, toPath string) {
|
||||||
|
if strings.Contains(to, "://") {
|
||||||
|
to = strings.Replace(to, "://", ":--", 1)
|
||||||
|
a := strings.SplitN(to, "/", 2)
|
||||||
|
if len(a) == 1 {
|
||||||
|
a = append(a, "")
|
||||||
|
}
|
||||||
|
a[0] = strings.Replace(a[0], ":--", "://", 1)
|
||||||
|
return a[0], "/" + a[1]
|
||||||
} else {
|
} else {
|
||||||
return &appName
|
if strings.HasPrefix(to, "/") {
|
||||||
|
toApp = ""
|
||||||
|
toPath = makeAppConfig(to)
|
||||||
|
} else {
|
||||||
|
a := strings.SplitN(to, "/", 2)
|
||||||
|
if len(a) == 1 {
|
||||||
|
a = append(a, "")
|
||||||
|
}
|
||||||
|
toApp = makeAppConfig(a[0])
|
||||||
|
toPath = "/" + a[1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeAppConfig(toStr string) (toApp string) {
|
||||||
|
toAppConfig := ""
|
||||||
|
if strings.ContainsRune(toApp, ':') {
|
||||||
|
a := strings.SplitN(toApp, ":", 2)
|
||||||
|
toApp = a[0]
|
||||||
|
toAppConfig = a[1]
|
||||||
|
} else {
|
||||||
|
toApp = toStr
|
||||||
|
// toAppConfig = (time.Duration(s.Config.RedirectTimeout) * time.Millisecond).String()
|
||||||
|
}
|
||||||
|
if discover.Config.Registry != "" {
|
||||||
|
discover.AddExternalApp(toApp, toAppConfig)
|
||||||
|
}
|
||||||
|
return toApp
|
||||||
}
|
}
|
||||||
|
4
go.mod
4
go.mod
@ -9,11 +9,11 @@ require (
|
|||||||
apigo.cc/gojs/util v0.0.2
|
apigo.cc/gojs/util v0.0.2
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/ssgo/config v1.7.7
|
github.com/ssgo/config v1.7.7
|
||||||
github.com/ssgo/discover v1.7.8
|
github.com/ssgo/discover v1.7.9
|
||||||
github.com/ssgo/httpclient v1.7.8
|
github.com/ssgo/httpclient v1.7.8
|
||||||
github.com/ssgo/log v1.7.7
|
github.com/ssgo/log v1.7.7
|
||||||
github.com/ssgo/redis v1.7.7
|
github.com/ssgo/redis v1.7.7
|
||||||
github.com/ssgo/s v1.7.14
|
github.com/ssgo/s v1.7.16
|
||||||
github.com/ssgo/standard v1.7.7
|
github.com/ssgo/standard v1.7.7
|
||||||
github.com/ssgo/u v1.7.9
|
github.com/ssgo/u v1.7.9
|
||||||
)
|
)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
req *s.Request
|
req *s.Request
|
||||||
|
Id string
|
||||||
Proto string
|
Proto string
|
||||||
Scheme string
|
Scheme string
|
||||||
Host string
|
Host string
|
||||||
@ -62,6 +63,7 @@ func MakeRequest(req *s.Request, args map[string]any, headers map[string]string)
|
|||||||
}
|
}
|
||||||
return gojs.MakeMap(&Request{
|
return gojs.MakeMap(&Request{
|
||||||
req: req,
|
req: req,
|
||||||
|
Id: req.Id,
|
||||||
Proto: req.Proto,
|
Proto: req.Proto,
|
||||||
Scheme: req.Header.Get(standard.DiscoverHeaderScheme),
|
Scheme: req.Header.Get(standard.DiscoverHeaderScheme),
|
||||||
Host: req.Header.Get(standard.DiscoverHeaderHost),
|
Host: req.Header.Get(standard.DiscoverHeaderHost),
|
||||||
|
@ -14,6 +14,7 @@ type Response struct {
|
|||||||
resp *s.Response
|
resp *s.Response
|
||||||
endCh chan bool
|
endCh chan bool
|
||||||
result any
|
result any
|
||||||
|
Id string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Response) End(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
func (r *Response) End(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
103
service.go
103
service.go
@ -70,8 +70,6 @@ var limiters = map[string]*s.Limiter{}
|
|||||||
func init() {
|
func init() {
|
||||||
obj := map[string]any{
|
obj := map[string]any{
|
||||||
"config": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
"config": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
s.Init()
|
|
||||||
|
|
||||||
// 处理配置
|
// 处理配置
|
||||||
args := gojs.MakeArgs(&argsIn, vm)
|
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{}}
|
serviceConfig = Config{"Session", "Device", "Client", "userId", "", 3600, "auth failed", "verify failed", "too many requests", nil, "", map[string]string{}, map[string]string{}, map[string]string{}}
|
||||||
@ -97,7 +95,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 身份验证和Session
|
// 身份验证和Session
|
||||||
authAccessToken := s.Config.AccessTokens != nil && len(s.Config.AccessTokens) > 0
|
authAccessToken := len(s.Config.AccessTokens) > 0
|
||||||
s.SetClientKeys(serviceConfig.DeviceKey, serviceConfig.ClientKey, serviceConfig.SessionKey)
|
s.SetClientKeys(serviceConfig.DeviceKey, serviceConfig.ClientKey, serviceConfig.SessionKey)
|
||||||
if serviceConfig.SessionKey != "" {
|
if serviceConfig.SessionKey != "" {
|
||||||
s.SetAuthChecker(func(authLevel int, logger *log.Logger, url *string, args map[string]any, request *s.Request, response *s.Response, options *s.WebServiceOptions) (pass bool, object any) {
|
s.SetAuthChecker(func(authLevel int, logger *log.Logger, url *string, args map[string]any, request *s.Request, response *s.Response, options *s.WebServiceOptions) (pass bool, object any) {
|
||||||
@ -105,6 +103,7 @@ func init() {
|
|||||||
setAuthLevel := 0
|
setAuthLevel := 0
|
||||||
if serviceConfig.SessionKey != "" {
|
if serviceConfig.SessionKey != "" {
|
||||||
sessionID := request.GetSessionId()
|
sessionID := request.GetSessionId()
|
||||||
|
|
||||||
if sessionID != "" {
|
if sessionID != "" {
|
||||||
session = NewSession(sessionID, logger)
|
session = NewSession(sessionID, logger)
|
||||||
}
|
}
|
||||||
@ -117,30 +116,9 @@ func init() {
|
|||||||
setAuthLevel = u.Int(authLevelBySession)
|
setAuthLevel = u.Int(authLevelBySession)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if auth != nil {
|
|
||||||
// requestParams, _ := makeRequestParams(args, nil, request, response, nil, session, logger)
|
|
||||||
// requestParams["authLevel"] = authLevel
|
|
||||||
// if r, err := auth(nil, vm.ToValue(requestParams)); err == nil {
|
|
||||||
// if r.ExportType().Kind() == reflect.Bool {
|
|
||||||
// if r.ToBoolean() {
|
|
||||||
// return true, session
|
|
||||||
// } else {
|
|
||||||
// return false, nil
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// return false, r.Export()
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// logger.Error(err.Error())
|
|
||||||
// return false, nil
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// return true, session
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
// 如果没有session中的authLevel验证失败,则使用Access-Token中的authLevel(服务间调用)
|
||||||
// 如果没有session或session中的authLevel为0,则使用Access-Token中的authLevel(服务间调用)
|
if authAccessToken && setAuthLevel < authLevel {
|
||||||
if authAccessToken && setAuthLevel == 0 && authLevel > 0 {
|
|
||||||
setAuthLevel = s.GetAuthTokenLevel(request.Header.Get("Access-Token"))
|
setAuthLevel = s.GetAuthTokenLevel(request.Header.Get("Access-Token"))
|
||||||
}
|
}
|
||||||
if setAuthLevel >= authLevel {
|
if setAuthLevel >= authLevel {
|
||||||
@ -159,6 +137,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
s.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 限流器
|
// 限流器
|
||||||
@ -189,17 +168,16 @@ func init() {
|
|||||||
if server != nil {
|
if server != nil {
|
||||||
panic(vm.NewGoError(errors.New("server already started")))
|
panic(vm.NewGoError(errors.New("server already started")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理静态文件
|
// 处理静态文件
|
||||||
if len(serviceConfig.Static) > 0 {
|
if len(serviceConfig.Static) > 0 {
|
||||||
updateStatic(serviceConfig.Static)
|
UpdateStatic(serviceConfig.Static)
|
||||||
}
|
}
|
||||||
if len(serviceConfig.Rewrite) > 0 {
|
if len(serviceConfig.Rewrite) > 0 {
|
||||||
updateRewrite(serviceConfig.Rewrite)
|
UpdateRewrite(serviceConfig.Rewrite)
|
||||||
s.SetRewriteBy(rewrite)
|
s.SetRewriteBy(rewrite)
|
||||||
}
|
}
|
||||||
if len(serviceConfig.Proxy) > 0 {
|
if len(serviceConfig.Proxy) > 0 {
|
||||||
updateProxy(serviceConfig.Proxy)
|
UpdateProxy(serviceConfig.Proxy)
|
||||||
s.SetProxyBy(proxy)
|
s.SetProxyBy(proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +201,7 @@ func init() {
|
|||||||
panic(vm.NewGoError(errors.New("server not started")))
|
panic(vm.NewGoError(errors.New("server not started")))
|
||||||
}
|
}
|
||||||
server.Stop()
|
server.Stop()
|
||||||
|
ClearRewritesAndProxies()
|
||||||
pools = map[string]*gojs.Pool{}
|
pools = map[string]*gojs.Pool{}
|
||||||
server = nil
|
server = nil
|
||||||
return nil
|
return nil
|
||||||
@ -366,7 +345,45 @@ func init() {
|
|||||||
poolsLock.Unlock()
|
poolsLock.Unlock()
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
"newCaller": NewCaller,
|
"task": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
taskFile := args.Path(0)
|
||||||
|
interval := args.Int(1)
|
||||||
|
if interval == 0 {
|
||||||
|
interval = 1000
|
||||||
|
}
|
||||||
|
if interval < 100 {
|
||||||
|
interval = 100
|
||||||
|
}
|
||||||
|
if !u.FileExists(taskFile) {
|
||||||
|
panic(vm.NewGoError(errors.New("taskFile must be a js file path")))
|
||||||
|
}
|
||||||
|
rt := gojs.New()
|
||||||
|
_, err := rt.RunFile(taskFile)
|
||||||
|
if err != nil {
|
||||||
|
panic(vm.NewGoError(err))
|
||||||
|
}
|
||||||
|
println(u.BMagenta("taskFile: "), taskFile, interval)
|
||||||
|
s.NewTimerServer(taskFile, time.Duration(interval)*time.Millisecond, func(isRunning *bool) {
|
||||||
|
rt.RunCode("if(onRun)onRun()")
|
||||||
|
}, func() {
|
||||||
|
rt.RunCode("if(onStart)onStart()")
|
||||||
|
}, func() {
|
||||||
|
rt.RunCode("if(onStop)onStop()")
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
"dataSet": DataSet,
|
||||||
|
"dataGet": DataGet,
|
||||||
|
"dataKeys": DataKeys,
|
||||||
|
"dataCount": DataCount,
|
||||||
|
"dataFetch": DataFetch,
|
||||||
|
"dataRemove": DataRemove,
|
||||||
|
"listPop": ListPop,
|
||||||
|
"listPush": ListPush,
|
||||||
|
"listCount": ListCount,
|
||||||
|
"listRemove": ListRemove,
|
||||||
|
"newCaller": NewCaller,
|
||||||
}
|
}
|
||||||
|
|
||||||
gojs.Register("apigo.cc/gojs/service", gojs.Module{
|
gojs.Register("apigo.cc/gojs/service", gojs.Module{
|
||||||
@ -436,16 +453,20 @@ func verifyFunc(callback goja.Callable, thisObj goja.Value) func(any, *goja.Runt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeRequestParams(args map[string]any, headers map[string]string, request *s.Request, response *s.Response, client *websocket.Conn, caller *discover.Caller, session *Session, logger *log.Logger) (gojs.Map, *Response) {
|
func makeRequestParams(args map[string]any, headers map[string]string, request *s.Request, response *s.Response, client *websocket.Conn, caller *discover.Caller, session *Session, logger *log.Logger) (gojs.Map, *Response) {
|
||||||
resp := &Response{
|
var resp *Response
|
||||||
resp: response,
|
|
||||||
endCh: make(chan bool, 1),
|
|
||||||
}
|
|
||||||
params := gojs.Map{
|
params := gojs.Map{
|
||||||
"args": args,
|
"args": args,
|
||||||
"logger": gojs.MakeLogger(logger),
|
"logger": gojs.MakeLogger(logger),
|
||||||
"request": MakeRequest(request, args, headers),
|
"request": MakeRequest(request, args, headers),
|
||||||
"response": gojs.MakeMap(resp),
|
"client": MakeWSClient(client, request.Id),
|
||||||
"client": MakeWSClient(client),
|
}
|
||||||
|
if response != nil {
|
||||||
|
resp = &Response{
|
||||||
|
resp: response,
|
||||||
|
endCh: make(chan bool, 1),
|
||||||
|
Id: response.Id,
|
||||||
|
}
|
||||||
|
params["response"] = gojs.MakeMap(resp)
|
||||||
}
|
}
|
||||||
if headers != nil {
|
if headers != nil {
|
||||||
params["headers"] = headers
|
params["headers"] = headers
|
||||||
@ -454,7 +475,7 @@ func makeRequestParams(args map[string]any, headers map[string]string, request *
|
|||||||
params["session"] = gojs.MakeMap(session)
|
params["session"] = gojs.MakeMap(session)
|
||||||
}
|
}
|
||||||
if caller != nil {
|
if caller != nil {
|
||||||
params["caller"] = gojs.MakeMap(Caller{client: caller})
|
params["caller"] = gojs.MakeMap(&Caller{client: caller})
|
||||||
}
|
}
|
||||||
return params, resp
|
return params, resp
|
||||||
}
|
}
|
||||||
|
47
service.ts
47
service.ts
@ -5,25 +5,43 @@ export default {
|
|||||||
start,
|
start,
|
||||||
stop,
|
stop,
|
||||||
register,
|
register,
|
||||||
load
|
load,
|
||||||
|
task,
|
||||||
|
newCaller,
|
||||||
|
dataSet,
|
||||||
|
dataGet,
|
||||||
|
dataKeys,
|
||||||
|
dataCount,
|
||||||
|
dataFetch,
|
||||||
|
dataRemove,
|
||||||
|
listPush,
|
||||||
|
listPop,
|
||||||
|
listCount,
|
||||||
|
listRemove,
|
||||||
}
|
}
|
||||||
|
|
||||||
function config(config?: Config): void {
|
function config(config?: Config): void { }
|
||||||
}
|
function start(): string { return '' }
|
||||||
|
function stop(): void { }
|
||||||
|
function register(option: RegisterOption, callback: (params: RequestParams) => void): any { return null }
|
||||||
|
function load(serviceFile: string, poolConfig?: PoolConfig): void { }
|
||||||
|
|
||||||
function start(): string {
|
function task(taskFile: string, interval: number = 1000): void { }
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
function stop(): void {
|
function newCaller(): Caller { return null as any }
|
||||||
}
|
|
||||||
|
|
||||||
function register(option: RegisterOption, callback: (params: RequestParams) => void): any {
|
function dataSet(scope: string, key: string, value: any): void { }
|
||||||
return null
|
function dataGet(scope: string, key: string): any { return null }
|
||||||
}
|
function dataKeys(scope: string): string[] { return [] }
|
||||||
|
function dataCount(scope: string): number { return 0 }
|
||||||
|
function dataFetch(scope: string): Map<string, any> { return null as any }
|
||||||
|
function dataRemove(scope: string, key?: string): void { }
|
||||||
|
|
||||||
|
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 load(serviceFile: string, poolConfig?: PoolConfig): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Config {
|
interface Config {
|
||||||
// github.com/ssgo/s 的配置参数
|
// github.com/ssgo/s 的配置参数
|
||||||
@ -151,6 +169,7 @@ interface OnMessageParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface WSClient {
|
interface WSClient {
|
||||||
|
id: string
|
||||||
read: () => WSMessage
|
read: () => WSMessage
|
||||||
write: (data: any) => void
|
write: (data: any) => void
|
||||||
writeMessage: (type: string, data: any) => void
|
writeMessage: (type: string, data: any) => void
|
||||||
@ -199,6 +218,7 @@ interface CookieOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
|
id: string
|
||||||
proto: string
|
proto: string
|
||||||
scheme: string
|
scheme: string
|
||||||
host: string
|
host: string
|
||||||
@ -227,6 +247,7 @@ interface Request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Response {
|
interface Response {
|
||||||
|
id: string
|
||||||
setStatus: (code: number) => void
|
setStatus: (code: number) => void
|
||||||
setCookie: (name: string, value: string, option?: CookieOption) => void
|
setCookie: (name: string, value: string, option?: CookieOption) => void
|
||||||
setHeader: (name: string, value: string) => void
|
setHeader: (name: string, value: string) => void
|
||||||
|
114
task.go
114
task.go
@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"apigo.cc/gojs"
|
"apigo.cc/gojs"
|
||||||
@ -10,7 +11,10 @@ import (
|
|||||||
var taskData = map[string]map[string]any{}
|
var taskData = map[string]map[string]any{}
|
||||||
var taskDataLock = sync.RWMutex{}
|
var taskDataLock = sync.RWMutex{}
|
||||||
|
|
||||||
func SetTaskData(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
var taskList = map[string]*list.List{}
|
||||||
|
var taskListLock = sync.RWMutex{}
|
||||||
|
|
||||||
|
func DataSet(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(3)
|
args := gojs.MakeArgs(&argsIn, vm).Check(3)
|
||||||
scope := args.Str(0)
|
scope := args.Str(0)
|
||||||
key := args.Str(1)
|
key := args.Str(1)
|
||||||
@ -24,7 +28,7 @@ func SetTaskData(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTaskData(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
func DataGet(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||||
scope := args.Str(0)
|
scope := args.Str(0)
|
||||||
key := args.Str(1)
|
key := args.Str(1)
|
||||||
@ -36,7 +40,7 @@ func GetTaskData(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTaskDataKeys(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
func DataKeys(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
scope := args.Str(0)
|
scope := args.Str(0)
|
||||||
taskDataLock.RLock()
|
taskDataLock.RLock()
|
||||||
@ -52,3 +56,107 @@ func GetTaskDataKeys(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DataCount(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
scope := args.Str(0)
|
||||||
|
taskDataLock.RLock()
|
||||||
|
defer taskDataLock.RUnlock()
|
||||||
|
if taskData[scope] != nil {
|
||||||
|
return vm.ToValue(len(taskData[scope]))
|
||||||
|
}
|
||||||
|
return vm.ToValue(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DataFetch(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
scope := args.Str(0)
|
||||||
|
taskDataLock.RLock()
|
||||||
|
defer taskDataLock.RUnlock()
|
||||||
|
if taskData[scope] != nil {
|
||||||
|
all := make(map[string]any)
|
||||||
|
for k, v := range taskData[scope] {
|
||||||
|
all[k] = v
|
||||||
|
}
|
||||||
|
return vm.ToValue(all)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DataRemove(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
scope := args.Str(0)
|
||||||
|
key := args.Str(1)
|
||||||
|
taskDataLock.Lock()
|
||||||
|
defer taskDataLock.Unlock()
|
||||||
|
if taskData[scope] != nil {
|
||||||
|
if key != "" {
|
||||||
|
delete(taskData[scope], key)
|
||||||
|
} else {
|
||||||
|
delete(taskData, scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListPush(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(2)
|
||||||
|
scope := args.Str(0)
|
||||||
|
value := args.Any(1)
|
||||||
|
fromHead := args.Bool(2)
|
||||||
|
taskListLock.Lock()
|
||||||
|
defer taskListLock.Unlock()
|
||||||
|
list1 := taskList[scope]
|
||||||
|
if list1 == nil {
|
||||||
|
list1 = list.New()
|
||||||
|
taskList[scope] = list1
|
||||||
|
}
|
||||||
|
if fromHead {
|
||||||
|
list1.PushFront(value)
|
||||||
|
} else {
|
||||||
|
list1.PushBack(value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListPop(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
scope := args.Str(0)
|
||||||
|
fromEnd := args.Bool(1)
|
||||||
|
taskListLock.Lock()
|
||||||
|
var item *list.Element
|
||||||
|
defer taskListLock.Unlock()
|
||||||
|
list1 := taskList[scope]
|
||||||
|
if list1 != nil {
|
||||||
|
if fromEnd {
|
||||||
|
item = list1.Front()
|
||||||
|
} else {
|
||||||
|
item = list1.Back()
|
||||||
|
}
|
||||||
|
if item != nil {
|
||||||
|
list1.Remove(item)
|
||||||
|
return vm.ToValue(item.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListCount(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
scope := args.Str(0)
|
||||||
|
taskListLock.RLock()
|
||||||
|
defer taskListLock.RUnlock()
|
||||||
|
if taskList[scope] != nil {
|
||||||
|
return vm.ToValue(taskList[scope].Len())
|
||||||
|
}
|
||||||
|
return vm.ToValue(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListRemove(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
|
args := gojs.MakeArgs(&argsIn, vm).Check(1)
|
||||||
|
scope := args.Str(0)
|
||||||
|
taskListLock.Lock()
|
||||||
|
defer taskListLock.Unlock()
|
||||||
|
delete(taskList, scope)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -14,7 +14,10 @@ function main() {
|
|||||||
session.save()
|
session.save()
|
||||||
return { code: 1 }
|
return { code: 1 }
|
||||||
})
|
})
|
||||||
service.register({ method: 'GET', path: '/userInfo', authLevel: 1, limiters: ['ip1s'] }, ({ session }) => {
|
service.register({ method: 'GET', path: '/userInfo', authLevel: 1, limiters: ['ip1s'] }, ({ caller }) => {
|
||||||
|
return caller.get('user/userInfoX').object()
|
||||||
|
})
|
||||||
|
service.register({ method: 'GET', path: '/userInfoX', authLevel: 2 }, ({ session }) => {
|
||||||
return { code: 1, data: session.get('id', 'name') }
|
return { code: 1, data: session.get('id', 'name') }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
import service from "apigo.cc/gojs/service"
|
import s from "apigo.cc/gojs/service"
|
||||||
import co from "apigo.cc/gojs/console"
|
import co from "apigo.cc/gojs/console"
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
service.register({
|
s.register({
|
||||||
method: 'WS', path: '/ws',
|
method: 'WS', path: '/ws',
|
||||||
onMessage: ({ client, type, data }) => {
|
onMessage: ({ client, type, data }) => {
|
||||||
client.writeMessage(type, data)
|
client.writeMessage(type, data)
|
||||||
},
|
},
|
||||||
onClose: () => {
|
onClose: ({ client }) => {
|
||||||
co.info('ws closed')
|
co.info('ws closed', client.id)
|
||||||
|
s.dataRemove('wsTest', client.id)
|
||||||
}
|
}
|
||||||
}, ({ client }) => {
|
}, ({ client }) => {
|
||||||
|
co.info('ws connected', client.id)
|
||||||
|
s.dataSet('wsTest', client.id, client)
|
||||||
client.write('Hello, World!')
|
client.write('Hello, World!')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
534
tests/gateway_test.go
Normal file
534
tests/gateway_test.go
Normal file
@ -0,0 +1,534 @@
|
|||||||
|
package service_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"apigo.cc/gojs/service"
|
||||||
|
"github.com/ssgo/s"
|
||||||
|
"github.com/ssgo/u"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRedirect(t *testing.T) {
|
||||||
|
type TestCase struct {
|
||||||
|
Url string
|
||||||
|
CheckToApp string
|
||||||
|
CheckToPath string
|
||||||
|
CheckOK bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestSet struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
Cases []TestCase
|
||||||
|
}
|
||||||
|
|
||||||
|
testSets := []TestSet{
|
||||||
|
{
|
||||||
|
Key: "https://abc.com:443/user/",
|
||||||
|
Value: "user",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "https://abc.com/user/",
|
||||||
|
Value: "user",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "abc.com/user/",
|
||||||
|
Value: "user",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "/user/",
|
||||||
|
Value: "user",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Key: "https://abc.com:443/user/(.*)",
|
||||||
|
Value: "user/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "https://abc.com/user/(.*)",
|
||||||
|
Value: "user/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "abc.com/user/(.*)",
|
||||||
|
Value: "user/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "/user/(.*)",
|
||||||
|
Value: "user/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "user",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Key: "https://abc.com:443/user/",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "https://abc.com/user/",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "abc.com/user/",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "/user/",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Key: "https://abc.com:443/user/(.*)",
|
||||||
|
Value: "https://def.com/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "https://abc.com/user/(.*)",
|
||||||
|
Value: "https://def.com/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "abc.com/user/(.*)",
|
||||||
|
Value: "https://def.com/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "/user/(.*)",
|
||||||
|
Value: "https://def.com/$1",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Key: "abc.com:443",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "https://abc.com",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "abc.com",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "/",
|
||||||
|
Value: "https://def.com",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "https://def.com",
|
||||||
|
CheckToPath: "/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Key: "https://abc.com:443",
|
||||||
|
Value: "/def/",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "abc.com:443",
|
||||||
|
Value: "/def/",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "https://abc.com",
|
||||||
|
Value: "/def/",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "abc.com",
|
||||||
|
Value: "/def/",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "/",
|
||||||
|
Value: "/def/",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/user/login",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/def/user/login",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "/aaa/111.txt",
|
||||||
|
Value: "/bbb/222/.txt",
|
||||||
|
Cases: []TestCase{
|
||||||
|
{
|
||||||
|
Url: "https://abc.com:443/aaa/111.txt",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/bbb/222/.txt",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Url: "https://abc.com/aaa/111.txt",
|
||||||
|
CheckToApp: "",
|
||||||
|
CheckToPath: "/bbb/222/.txt",
|
||||||
|
CheckOK: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
makeRequest := func(url string) *s.Request {
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
return &s.Request{Request: req}
|
||||||
|
}
|
||||||
|
|
||||||
|
// s.ServerLogger = log.NewLogger(log.Config{File: "/dev/null"})
|
||||||
|
for _, testSet := range testSets {
|
||||||
|
service.ClearRewritesAndProxies()
|
||||||
|
service.UpdateProxy(map[string]string{testSet.Key: testSet.Value})
|
||||||
|
for _, testCase := range testSet.Cases {
|
||||||
|
toApp, toPath, ok := service.MatchProxy(makeRequest(testCase.Url))
|
||||||
|
if toApp != testCase.CheckToApp || toPath != testCase.CheckToPath || ok != testCase.CheckOK {
|
||||||
|
c1 := u.TextGreen
|
||||||
|
c2 := u.TextGreen
|
||||||
|
c3 := u.TextGreen
|
||||||
|
if toApp != testCase.CheckToApp {
|
||||||
|
c1 = u.TextRed
|
||||||
|
}
|
||||||
|
if toPath != testCase.CheckToPath {
|
||||||
|
c2 = u.TextRed
|
||||||
|
}
|
||||||
|
if ok != testCase.CheckOK {
|
||||||
|
c3 = u.TextRed
|
||||||
|
}
|
||||||
|
fmt.Println(u.BRed("test failed"), testCase.Url, u.Green(testCase.CheckToApp), u.Color(toApp, c1, u.BgNone), u.Green(testCase.CheckToPath), u.Color(toPath, c2, u.BgNone), u.Green(testCase.CheckOK), u.Color(ok, c3, u.BgNone))
|
||||||
|
} else {
|
||||||
|
fmt.Println(u.BGreen("test success"), testCase.Url, u.Green(testCase.CheckToApp), u.BGreen(toApp), u.Green(testCase.CheckToPath), u.BGreen(toPath), u.Green(testCase.CheckOK), u.BGreen(ok))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
service.ClearRewritesAndProxies()
|
||||||
|
}
|
@ -11,6 +11,7 @@ import (
|
|||||||
"apigo.cc/gojs/service"
|
"apigo.cc/gojs/service"
|
||||||
_ "apigo.cc/gojs/service"
|
_ "apigo.cc/gojs/service"
|
||||||
_ "apigo.cc/gojs/util"
|
_ "apigo.cc/gojs/util"
|
||||||
|
"github.com/ssgo/discover"
|
||||||
"github.com/ssgo/httpclient"
|
"github.com/ssgo/httpclient"
|
||||||
"github.com/ssgo/u"
|
"github.com/ssgo/u"
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ func TestStartByPool(t *testing.T) {
|
|||||||
t.Fatal("start failed", err)
|
t.Fatal("start failed", err)
|
||||||
}
|
}
|
||||||
addrByPool = u.String(r)
|
addrByPool = u.String(r)
|
||||||
|
discover.SetNode("user", addrByPool, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Caller
|
// TODO Caller
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package service_test
|
package service_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -10,7 +9,6 @@ import (
|
|||||||
_ "apigo.cc/gojs/http"
|
_ "apigo.cc/gojs/http"
|
||||||
_ "apigo.cc/gojs/service"
|
_ "apigo.cc/gojs/service"
|
||||||
_ "apigo.cc/gojs/util"
|
_ "apigo.cc/gojs/util"
|
||||||
"github.com/ssgo/httpclient"
|
|
||||||
"github.com/ssgo/u"
|
"github.com/ssgo/u"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,80 +31,90 @@ func TestStart(t *testing.T) {
|
|||||||
addr = u.String(r)
|
addr = u.String(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJsEcho(t *testing.T) {
|
func TestStatic(t *testing.T) {
|
||||||
for i := 0; i < runTimes; i++ {
|
r, err := rt.RunCode("testStatic()")
|
||||||
name := u.UniqueId()
|
if err != nil {
|
||||||
r, err := rt.RunCode("test('" + name + "')")
|
t.Fatal("test static failed, got error", err)
|
||||||
if err != nil {
|
}
|
||||||
t.Fatal("test js get failed, got error", err)
|
if r != true {
|
||||||
} else if r != name {
|
t.Fatal("test static failed, name not match", r)
|
||||||
t.Fatal("test js get failed, name not match", r, name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGoEcho(t *testing.T) {
|
// func TestJsEcho(t *testing.T) {
|
||||||
hc := httpclient.GetClientH2C(0)
|
// for i := 0; i < runTimes; i++ {
|
||||||
for i := 0; i < runTimes; i++ {
|
// name := u.UniqueId()
|
||||||
name := u.UniqueId()
|
// r, err := rt.RunCode("test('" + name + "')")
|
||||||
r := hc.Get("http://" + addr + "/echo?name=" + name)
|
// if err != nil {
|
||||||
if r.Error != nil {
|
// t.Fatal("test js get failed, got error", err)
|
||||||
t.Fatal("test go get failed, got error", r.Error)
|
// } else if r != name {
|
||||||
} else if r.String() != name {
|
// t.Fatal("test js get failed, name not match", r, name)
|
||||||
t.Fatal("test go get failed, name not match", r, name)
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
func TestJsAsyncEcho(t *testing.T) {
|
// func TestGoEcho(t *testing.T) {
|
||||||
ch := make(chan bool, runTimes)
|
// hc := httpclient.GetClientH2C(0)
|
||||||
t1 := time.Now().UnixMilli()
|
// for i := 0; i < runTimes; i++ {
|
||||||
for i := 0; i < runTimes; i++ {
|
// name := u.UniqueId()
|
||||||
go func() {
|
// r := hc.Get("http://" + addr + "/echo?name=" + name)
|
||||||
name := u.UniqueId()
|
// if r.Error != nil {
|
||||||
r, err := rt.RunCode("test('" + name + "')")
|
// t.Fatal("test go get failed, got error", r.Error)
|
||||||
ch <- true
|
// } else if r.String() != name {
|
||||||
if err != nil {
|
// t.Fatal("test go get failed, name not match", r, name)
|
||||||
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) {
|
// func TestJsAsyncEcho(t *testing.T) {
|
||||||
hc := httpclient.GetClientH2C(0)
|
// ch := make(chan bool, runTimes)
|
||||||
ch := make(chan bool, runTimes*10)
|
// t1 := time.Now().UnixMilli()
|
||||||
t1 := time.Now().UnixMilli()
|
// for i := 0; i < runTimes; i++ {
|
||||||
lastName := ""
|
// go func() {
|
||||||
lastResult := ""
|
// name := u.UniqueId()
|
||||||
for i := 0; i < runTimes*10; i++ {
|
// r, err := rt.RunCode("test('" + name + "')")
|
||||||
name := fmt.Sprint("N", i)
|
// ch <- true
|
||||||
lastName = name
|
// if err != nil {
|
||||||
go func() {
|
// t.Fatal("test js async get failed, got error", err)
|
||||||
r := hc.Get("http://" + addr + "/echo?name=" + name)
|
// } else if r != name {
|
||||||
lastResult = r.String()
|
// t.Fatal("test js async get failed, name not match", r, name)
|
||||||
ch <- true
|
// }
|
||||||
if r.Error != nil {
|
// }()
|
||||||
t.Fatal("test go async get failed, got error", r.Error)
|
// }
|
||||||
} else if r.String() != name {
|
// for i := 0; i < runTimes; i++ {
|
||||||
t.Fatal("test go async get failed, name not match", r, name)
|
// <-ch
|
||||||
}
|
// }
|
||||||
}()
|
// t2 := time.Now().UnixMilli() - t1
|
||||||
}
|
// fmt.Println(u.BGreen("js async test time:"), t2, "ms")
|
||||||
for i := 0; i < runTimes*10; i++ {
|
// }
|
||||||
<-ch
|
|
||||||
}
|
// func TestGoAsyncEcho(t *testing.T) {
|
||||||
t2 := time.Now().UnixMilli() - t1
|
// hc := httpclient.GetClientH2C(0)
|
||||||
fmt.Println(u.BGreen("go async test time:"), t2, "ms")
|
// ch := make(chan bool, runTimes*10)
|
||||||
fmt.Println(u.BGreen("last name:"), lastName, lastResult)
|
// 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) {
|
func TestStop(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -7,6 +7,13 @@ let h2c = http
|
|||||||
let urlPrefix
|
let urlPrefix
|
||||||
function main() {
|
function main() {
|
||||||
s.config({
|
s.config({
|
||||||
|
app: 'user',
|
||||||
|
accessTokens: {
|
||||||
|
'testToken1122': 2
|
||||||
|
},
|
||||||
|
calls: {
|
||||||
|
'user': 'testToken1122'
|
||||||
|
},
|
||||||
cpuMonitor: true,
|
cpuMonitor: true,
|
||||||
memoryMonitor: true,
|
memoryMonitor: true,
|
||||||
sessionKey: 'SessionID',
|
sessionKey: 'SessionID',
|
||||||
@ -21,6 +28,12 @@ function main() {
|
|||||||
times: 10
|
times: 10
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
static: {
|
||||||
|
'/': 'api/',
|
||||||
|
},
|
||||||
|
rewrite: {
|
||||||
|
'/echo2.js': '/echo.js'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
s.register({ path: '/echo', noLog200: true }, ({ args, response }) => {
|
s.register({ path: '/echo', noLog200: true }, ({ args, response }) => {
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
@ -43,6 +56,12 @@ function test(name) {
|
|||||||
return r.string()
|
return r.string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testStatic() {
|
||||||
|
let r = h2c.get('/echo2.js')
|
||||||
|
if (r.string().indexOf('/echo2') === -1) return r.string()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
function test2(name) {
|
function test2(name) {
|
||||||
let r = h2c.get('/echo2?name=' + name)
|
let r = h2c.get('/echo2?name=' + name)
|
||||||
return r.string()
|
return r.string()
|
||||||
|
@ -7,6 +7,7 @@ let hc = http
|
|||||||
let urlPrefix
|
let urlPrefix
|
||||||
function main() {
|
function main() {
|
||||||
s.load('api/ws.js')
|
s.load('api/ws.js')
|
||||||
|
s.task('task.js', 100)
|
||||||
let host = s.start()
|
let host = s.start()
|
||||||
hc = http.new({ baseURL: 'http://' + host })
|
hc = http.new({ baseURL: 'http://' + host })
|
||||||
return host
|
return host
|
||||||
@ -30,7 +31,7 @@ function testWS() {
|
|||||||
co.info('test ws abc ok')
|
co.info('test ws abc ok')
|
||||||
|
|
||||||
// ws.ping()
|
// ws.ping()
|
||||||
u.sleep(100)
|
u.sleep(10)
|
||||||
|
|
||||||
let pc = ws.pingCount()
|
let pc = ws.pingCount()
|
||||||
co.info('test ws ping ok', pc.pingTimes, pc.pongTimes)
|
co.info('test ws ping ok', pc.pingTimes, pc.pongTimes)
|
||||||
@ -51,6 +52,14 @@ function testWS() {
|
|||||||
}
|
}
|
||||||
co.info('test ws json ok')
|
co.info('test ws json ok')
|
||||||
|
|
||||||
|
u.sleep(1000)
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
let j = ws.read()
|
||||||
|
if (i !== j.data) {
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ws.close()
|
ws.close()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
30
tests/task.js
Normal file
30
tests/task.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import s from 'apigo.cc/gojs/service'
|
||||||
|
import co from 'apigo.cc/gojs/console'
|
||||||
|
|
||||||
|
function onStart() {
|
||||||
|
co.info('task start')
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
function onRun() {
|
||||||
|
let connCount = s.dataCount('wsTest')
|
||||||
|
if (connCount > 0) {
|
||||||
|
let conns = s.dataFetch('wsTest')
|
||||||
|
for (let id in conns) {
|
||||||
|
let conn = conns[id]
|
||||||
|
try {
|
||||||
|
conn.write(i++)
|
||||||
|
} catch (e) {
|
||||||
|
co.error(e)
|
||||||
|
s.dataRemove('wsTest', id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
co.info('task run', connCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function onStop() {
|
||||||
|
s.dataRemove('wsTest')
|
||||||
|
co.info('task stop', s.dataCount('wsTest'))
|
||||||
|
}
|
3
ws.go
3
ws.go
@ -12,8 +12,9 @@ import (
|
|||||||
"github.com/ssgo/u"
|
"github.com/ssgo/u"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeWSClient(client *websocket.Conn) gojs.Map {
|
func MakeWSClient(client *websocket.Conn, id string) gojs.Map {
|
||||||
return gojs.Map{
|
return gojs.Map{
|
||||||
|
"id": id,
|
||||||
"read": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
"read": func(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
|
||||||
typ, data, err := readWSMessage(client)
|
typ, data, err := readWSMessage(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user