update ws

This commit is contained in:
Star 2024-10-16 18:08:06 +08:00
parent 4412bfc727
commit ea04fd82c8
3 changed files with 143 additions and 70 deletions

156
http.go
View File

@ -2,6 +2,7 @@ package http
import ( import (
_ "embed" _ "embed"
"encoding/json"
"net/http" "net/http"
"reflect" "reflect"
"strings" "strings"
@ -275,14 +276,14 @@ func (hc *Http) Connect(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
// ws.onPing = opt.Func("onPing") // ws.onPing = opt.Func("onPing")
// ws.onPong = opt.Func("onPong") // ws.onPong = opt.Func("onPong")
ws.onMessage = opt.Func("onMessage") ws.onMessage = opt.Func("onMessage")
ws.onJSONMessage = opt.Func("onJSONMessage")
ws.pingInterval = opt.Int64("pingInterval") ws.pingInterval = opt.Int64("pingInterval")
ws.reconnectInterval = opt.Int64("reconnectInterval") ws.reconnectInterval = opt.Int64("reconnectInterval")
if ws.reconnectInterval == 0 && (ws.onMessage != nil || ws.onJSONMessage != nil) { if ws.reconnectInterval == 0 && ws.onMessage != nil {
ws.reconnectInterval = 1000 ws.reconnectInterval = 1000
} }
} }
// fmt.Println(u.BMagenta("WS"), "start") // fmt.Println(u.BMagenta("WS"), "start")
if err := ws.connect(vm); err == nil { if err := ws.connect(vm); err == nil {
if ws.pingInterval > 0 { if ws.pingInterval > 0 {
// fmt.Println(u.BMagenta("WS"), "start ping") // fmt.Println(u.BMagenta("WS"), "start ping")
@ -290,10 +291,7 @@ func (hc *Http) Connect(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
// ws.sleep(time.Duration(ws.pingInterval) * time.Millisecond) // ws.sleep(time.Duration(ws.pingInterval) * time.Millisecond)
for { for {
if ws.conn != nil { if ws.conn != nil {
// fmt.Println(u.BMagenta("WS"), "ping") ws.writeMessage(websocket.PingMessage, nil)
ws.writeLock.Lock()
ws.conn.WriteMessage(websocket.PingMessage, []byte{'P'})
ws.writeLock.Unlock()
} }
if !ws.running { if !ws.running {
break break
@ -310,30 +308,17 @@ func (hc *Http) Connect(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
ws.pingStopChan <- true ws.pingStopChan <- true
} }
if ws.onMessage != nil || ws.onJSONMessage != nil { if ws.onMessage != nil {
// fmt.Println(u.BMagenta("WS"), "start onMessage") // fmt.Println(u.BMagenta("WS"), "start onMessage")
go func() { go func() {
for { for {
for { for {
if ws.conn != nil { if ws.conn != nil {
if ws.onJSONMessage != nil { typ, data, err := readWSMessage(ws.conn)
var obj interface{}
err := ws.conn.ReadJSON(&obj)
if err != nil { if err != nil {
break break
} }
_, _ = ws.onJSONMessage(ws.this, vm.ToValue(obj)) _, _ = ws.onMessage(ws.this, vm.ToValue(typ), vm.ToValue(data))
} else {
typ, buf, err := ws.conn.ReadMessage()
if err != nil {
break
}
if typ == websocket.TextMessage {
_, _ = ws.onMessage(ws.this, vm.ToValue(string(buf)))
} else {
_, _ = ws.onMessage(ws.this, vm.ToValue(buf))
}
}
} }
} }
// fmt.Println(u.BMagenta("WS"), "stop onMessage") // fmt.Println(u.BMagenta("WS"), "stop onMessage")
@ -377,8 +362,11 @@ type WS struct {
onError goja.Callable onError goja.Callable
// onPing goja.Callable // onPing goja.Callable
// onPong goja.Callable // onPong goja.Callable
pingTimes int64
pongTimes int64
lastPingTime int64
lastPongTime int64
onMessage goja.Callable onMessage goja.Callable
onJSONMessage goja.Callable
pingInterval int64 pingInterval int64
reconnectInterval int64 reconnectInterval int64
} }
@ -437,6 +425,15 @@ func (ws *WS) connect(vm *goja.Runtime) error {
// }) // })
// return err // return err
// } // }
ws.pingTimes = 0
ws.pongTimes = 0
ws.lastPingTime = 0
ws.lastPongTime = 0
conn.SetPongHandler(func(appData string) error {
ws.pongTimes++
ws.lastPongTime = time.Now().UnixMilli()
return nil
})
conn.SetCloseHandler(func(code int, text string) error { conn.SetCloseHandler(func(code int, text string) error {
// fmt.Println(u.BMagenta("WS"), "onClose") // fmt.Println(u.BMagenta("WS"), "onClose")
if ws.onClose != nil { if ws.onClose != nil {
@ -462,48 +459,111 @@ func (ws *WS) connect(vm *goja.Runtime) error {
return nil return nil
} }
func (ws *WS) Read(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { func (ws *WS) PingCount(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
typ, buf, err := ws.conn.ReadMessage() return vm.ToValue(gojs.Map{
if err != nil { "pingTimes": ws.pingTimes,
panic(vm.NewGoError(err)) "pongTimes": ws.pongTimes,
} "lastPingTime": ws.lastPingTime,
if typ == websocket.TextMessage { "lastPongTime": ws.lastPongTime,
return vm.ToValue(string(buf)) })
} else {
return vm.ToValue(buf)
}
} }
func (ws *WS) ReadJSON(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { func (ws *WS) Read(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
var obj interface{} typ, data, err := readWSMessage(ws.conn)
err := ws.conn.ReadJSON(&obj)
if err != nil { if err != nil {
panic(vm.NewGoError(err)) panic(vm.NewGoError(err))
} }
return vm.ToValue(obj) return vm.ToValue(gojs.Map{
"type": typ,
"data": data,
})
}
func readWSMessage(client *websocket.Conn) (string, any, error) {
msgType := "close"
var msgData any
typ, data, err := client.ReadMessage()
if err == nil && typ != websocket.CloseMessage {
switch typ {
case websocket.TextMessage:
if err2 := json.Unmarshal(data, &msgData); err2 != nil {
msgData = string(data)
msgType = "text"
} else {
msgType = "json"
}
case websocket.BinaryMessage:
if err2 := json.Unmarshal(data, &msgData); err2 != nil {
msgData = data
msgType = "binary"
} else {
msgType = "json"
}
case websocket.PingMessage:
msgData = string(data)
msgType = "ping"
case websocket.PongMessage:
msgData = string(data)
msgType = "pong"
}
}
return msgType, msgData, err
}
func (ws *WS) writeMessage(typ int, msg []byte) error {
ws.writeLock.Lock()
defer ws.writeLock.Unlock()
if typ == websocket.PingMessage {
ws.pingTimes++
ws.lastPingTime = time.Now().UnixMilli()
}
return ws.conn.WriteMessage(typ, msg)
} }
func (ws *WS) Write(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { func (ws *WS) Write(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(1) args := gojs.MakeArgs(&argsIn, vm).Check(1)
var err error var err error
ws.writeLock.Lock() switch args.Any(0).(type) {
if args.Arguments[0].ExportType().Kind() == reflect.String { case []byte:
err = ws.conn.WriteMessage(websocket.TextMessage, args.Bytes(0)) err = ws.writeMessage(websocket.BinaryMessage, args.Bytes(0))
} else { case string:
err = ws.conn.WriteMessage(websocket.BinaryMessage, args.Bytes(0)) err = ws.writeMessage(websocket.TextMessage, args.Bytes(0))
default:
err = ws.writeMessage(websocket.TextMessage, u.JsonBytes(args.Any(0)))
} }
ws.writeLock.Unlock()
if err != nil { if err != nil {
panic(vm.NewGoError(err)) panic(vm.NewGoError(err))
} }
return nil return nil
} }
func (ws *WS) WriteJSON(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value { func (ws *WS) Ping(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(1) err := ws.writeMessage(websocket.PingMessage, nil)
ws.writeLock.Lock() if err != nil {
err := ws.conn.WriteJSON(args.Any(0)) panic(vm.NewGoError(err))
ws.writeLock.Unlock() }
return nil
}
func (ws *WS) WriteMessage(argsIn goja.FunctionCall, vm *goja.Runtime) goja.Value {
args := gojs.MakeArgs(&argsIn, vm).Check(2)
var err error
switch args.Str(0) {
case "text":
err = ws.writeMessage(websocket.TextMessage, args.Bytes(1))
case "binary":
err = ws.writeMessage(websocket.BinaryMessage, args.Bytes(1))
case "ping":
err = ws.writeMessage(websocket.PingMessage, args.Bytes(1))
case "pong":
err = ws.writeMessage(websocket.PongMessage, args.Bytes(1))
case "close":
err = ws.writeMessage(websocket.CloseMessage, args.Bytes(1))
case "json":
err = ws.writeMessage(websocket.TextMessage, u.JsonBytes(args.Any(1)))
default:
err = ws.writeMessage(websocket.TextMessage, args.Bytes(1))
}
if err != nil { if err != nil {
panic(vm.NewGoError(err)) panic(vm.NewGoError(err))
} }

22
http.ts
View File

@ -59,16 +59,28 @@ interface WSConfig {
compress: boolean compress: boolean
onOpen: () => void onOpen: () => void
onClose: (code: number, data: string) => void onClose: (code: number, data: string) => void
onMessage: (data: any) => void onMessage: (type: string, data: any) => void
onJSONMessage: (data: any) => void
pingInterval: number pingInterval: number
reconnectInterval: number reconnectInterval: number
} }
interface WS { interface WS {
read(): any read: () => WSMessage
readJSON(): any
write(data: any): void write(data: any): void
writeJSON(data: any): void writeMessage(type: string, data: any): void
ping: () => void
pingCount: () => PingCount
close(): void close(): void
} }
interface PingCount {
pingTimes: number
pongTimes: number
lastPingTime: number
lastPongTime: number
}
interface WSMessage {
type: string
data: string | Object
}

View File

@ -6,20 +6,21 @@ function main() { }
function testSync() { function testSync() {
let conn = http.connect("http://127.0.0.1:18001/ws") let conn = http.connect("http://127.0.0.1:18001/ws")
conn.write('111') conn.write('111')
if (conn.read() !== '111') { let r1 = conn.read().data
return false if (r1 !== 111) {
return r1
} }
conn.write(new Uint8Array([0x01, 0x02, 0x03])) conn.write(new Uint8Array([0x01, 0x02, 0x03]))
let r2 = conn.read() let r2 = conn.read().data
if (r2[0] != 1 || r2[1] != 2 || r2[2] != 3) { if (r2[0] != 1 || r2[1] != 2 || r2[2] != 3) {
return false return r2
} }
conn.writeJSON({ name: 'Tom' }) conn.write({ name: 'Tom' })
let r3 = conn.readJSON() let r3 = conn.read().data
if (r3.name != 'Tom') { if (r3.name != 'Tom') {
return false return r3
} }
conn.close() conn.close()
return true return true
@ -42,22 +43,22 @@ function testAsync() {
onPong: function (data) { onPong: function (data) {
console.info("onPong", data) console.info("onPong", data)
}, },
onJSONMessage: function (data) { onMessage: function (type, data) {
// console.info("onJSONMessage", data) // console.info("onJSONMessage", data)
asyncResult += data.name asyncResult += data.name
}, },
}) })
asyncConn = conn asyncConn = conn
conn.writeJSON({ name: 'Tom1' }) conn.write({ name: 'Tom1' })
conn.writeJSON({ name: 'Tom2' }) conn.write({ name: 'Tom2' })
conn.writeJSON({ name: 'Tom3' }) conn.write({ name: 'Tom3' })
} }
function testAsync2() { function testAsync2() {
asyncConn.writeJSON({ name: 'Tom4' }) asyncConn.write({ name: 'Tom4' })
asyncConn.writeJSON({ name: 'Tom5' }) asyncConn.write({ name: 'Tom5' })
asyncConn.writeJSON({ name: 'Tom6' }) asyncConn.write({ name: 'Tom6' })
} }
function closeAsync() { function closeAsync() {