diff options
Diffstat (limited to 'vendor/github.com/playwright-community/playwright-go/websocket_route.go')
| -rw-r--r-- | vendor/github.com/playwright-community/playwright-go/websocket_route.go | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/vendor/github.com/playwright-community/playwright-go/websocket_route.go b/vendor/github.com/playwright-community/playwright-go/websocket_route.go new file mode 100644 index 0000000..bb74ab9 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/websocket_route.go @@ -0,0 +1,220 @@ +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 +} |
