dt_automate/vendor/github.com/playwright-community/playwright-go/websocket_route.go
2025-02-19 18:30:19 +08:00

221 lines
6.1 KiB
Go

package playwright
import (
"encoding/base64"
"fmt"
"regexp"
"sync/atomic"
)
type webSocketRouteImpl struct {
channelOwner
connected *atomic.Bool
server WebSocketRoute
onPageMessage func(interface{})
onPageClose func(code *int, reason *string)
onServerMessage func(interface{})
onServerClose func(code *int, reason *string)
}
func newWebSocketRoute(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *webSocketRouteImpl {
route := &webSocketRouteImpl{
connected: &atomic.Bool{},
}
route.createChannelOwner(route, parent, objectType, guid, initializer)
route.markAsInternalType()
route.server = newServerWebSocketRoute(route)
route.channel.On("messageFromPage", func(event map[string]interface{}) {
msg, err := untransformWebSocketMessage(event)
if err != nil {
panic(fmt.Errorf("Could not decode WebSocket message: %w", err))
}
if route.onPageMessage != nil {
route.onPageMessage(msg)
} else if route.connected.Load() {
go route.channel.SendNoReply("sendToServer", event)
}
})
route.channel.On("messageFromServer", func(event map[string]interface{}) {
msg, err := untransformWebSocketMessage(event)
if err != nil {
panic(fmt.Errorf("Could not decode WebSocket message: %w", err))
}
if route.onServerMessage != nil {
route.onServerMessage(msg)
} else {
go route.channel.SendNoReply("sendToPage", event)
}
})
route.channel.On("closePage", func(event map[string]interface{}) {
if route.onPageClose != nil {
route.onPageClose(event["code"].(*int), event["reason"].(*string))
} else {
go route.channel.SendNoReply("closeServer", event)
}
})
route.channel.On("closeServer", func(event map[string]interface{}) {
if route.onServerClose != nil {
route.onServerClose(event["code"].(*int), event["reason"].(*string))
} else {
go route.channel.SendNoReply("closePage", event)
}
})
return route
}
func (r *webSocketRouteImpl) Close(options ...WebSocketRouteCloseOptions) {
r.channel.SendNoReply("closePage", options, map[string]interface{}{"wasClean": true})
}
func (r *webSocketRouteImpl) ConnectToServer() (WebSocketRoute, error) {
if r.connected.Load() {
return nil, fmt.Errorf("Already connected to the server")
}
r.channel.SendNoReply("connect")
r.connected.Store(true)
return r.server, nil
}
func (r *webSocketRouteImpl) OnClose(handler func(code *int, reason *string)) {
r.onPageClose = handler
}
func (r *webSocketRouteImpl) OnMessage(handler func(interface{})) {
r.onPageMessage = handler
}
func (r *webSocketRouteImpl) Send(message interface{}) {
data, err := transformWebSocketMessage(message)
if err != nil {
panic(fmt.Errorf("Could not encode WebSocket message: %w", err))
}
go r.channel.SendNoReply("sendToPage", data)
}
func (r *webSocketRouteImpl) URL() string {
return r.initializer["url"].(string)
}
func (r *webSocketRouteImpl) afterHandle() error {
if r.connected.Load() {
return nil
}
// Ensure that websocket is "open" and can send messages without an actual server connection.
_, err := r.channel.Send("ensureOpened")
return err
}
type serverWebSocketRouteImpl struct {
webSocketRoute *webSocketRouteImpl
}
func newServerWebSocketRoute(route *webSocketRouteImpl) *serverWebSocketRouteImpl {
return &serverWebSocketRouteImpl{webSocketRoute: route}
}
func (s *serverWebSocketRouteImpl) OnMessage(handler func(interface{})) {
s.webSocketRoute.onServerMessage = handler
}
func (s *serverWebSocketRouteImpl) OnClose(handler func(code *int, reason *string)) {
s.webSocketRoute.onServerClose = handler
}
func (s *serverWebSocketRouteImpl) ConnectToServer() (WebSocketRoute, error) {
return nil, fmt.Errorf("ConnectToServer must be called on the page-side WebSocketRoute")
}
func (s *serverWebSocketRouteImpl) URL() string {
return s.webSocketRoute.URL()
}
func (s *serverWebSocketRouteImpl) Close(options ...WebSocketRouteCloseOptions) {
go s.webSocketRoute.channel.SendNoReply("close", options, map[string]interface{}{"wasClean": true})
}
func (s *serverWebSocketRouteImpl) Send(message interface{}) {
data, err := transformWebSocketMessage(message)
if err != nil {
panic(fmt.Errorf("Could not encode WebSocket message: %w", err))
}
go s.webSocketRoute.channel.SendNoReply("sendToServer", data)
}
func transformWebSocketMessage(message interface{}) (map[string]interface{}, error) {
data := map[string]interface{}{}
switch v := message.(type) {
case []byte:
data["isBase64"] = true
data["message"] = base64.StdEncoding.EncodeToString(v)
case string:
data["isBase64"] = false
data["message"] = v
default:
return nil, fmt.Errorf("Unsupported message type: %T", v)
}
return data, nil
}
func untransformWebSocketMessage(data map[string]interface{}) (interface{}, error) {
if data["isBase64"].(bool) {
return base64.StdEncoding.DecodeString(data["message"].(string))
}
return data["message"], nil
}
type webSocketRouteHandler struct {
matcher *urlMatcher
handler func(WebSocketRoute)
}
func newWebSocketRouteHandler(matcher *urlMatcher, handler func(WebSocketRoute)) *webSocketRouteHandler {
return &webSocketRouteHandler{matcher: matcher, handler: handler}
}
func (h *webSocketRouteHandler) Handle(route WebSocketRoute) {
h.handler(route)
err := route.(*webSocketRouteImpl).afterHandle()
if err != nil {
panic(fmt.Errorf("Could not handle WebSocketRoute: %w", err))
}
}
func (h *webSocketRouteHandler) Matches(wsURL string) bool {
return h.matcher.Matches(wsURL)
}
func prepareWebSocketRouteHandlerInterceptionPatterns(handlers []*webSocketRouteHandler) []map[string]interface{} {
patterns := []map[string]interface{}{}
all := false
for _, handler := range handlers {
switch handler.matcher.raw.(type) {
case *regexp.Regexp:
pattern, flags := convertRegexp(handler.matcher.raw.(*regexp.Regexp))
patterns = append(patterns, map[string]interface{}{
"regexSource": pattern,
"regexFlags": flags,
})
case string:
patterns = append(patterns, map[string]interface{}{
"glob": handler.matcher.raw.(string),
})
default:
all = true
}
}
if all {
return []map[string]interface{}{
{
"glob": "**/*",
},
}
}
return patterns
}