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

142 lines
3.0 KiB
Go

package playwright
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"os"
"github.com/go-jose/go-jose/v3/json"
)
type transport interface {
Send(msg map[string]interface{}) error
Poll() (*message, error)
Close() error
}
type pipeTransport struct {
writer io.WriteCloser
bufReader *bufio.Reader
closed chan struct{}
onClose func() error
}
func (t *pipeTransport) Poll() (*message, error) {
if t.isClosed() {
return nil, fmt.Errorf("transport closed")
}
var length uint32
err := binary.Read(t.bufReader, binary.LittleEndian, &length)
if err != nil {
return nil, fmt.Errorf("could not read protocol padding: %w", err)
}
data := make([]byte, length)
_, err = io.ReadFull(t.bufReader, data)
if err != nil {
return nil, fmt.Errorf("could not read protocol data: %w", err)
}
msg := &message{}
if err := json.Unmarshal(data, &msg); err != nil {
return nil, fmt.Errorf("could not decode json: %w", err)
}
if os.Getenv("DEBUGP") != "" {
fmt.Fprintf(os.Stdout, "\x1b[33mRECV>\x1b[0m\n%s\n", data)
}
return msg, nil
}
type message struct {
ID int `json:"id"`
GUID string `json:"guid"`
Method string `json:"method,omitempty"`
Params map[string]interface{} `json:"params,omitempty"`
Result map[string]interface{} `json:"result,omitempty"`
Error *struct {
Error Error `json:"error"`
} `json:"error,omitempty"`
}
func (t *pipeTransport) Send(msg map[string]interface{}) error {
if t.isClosed() {
return fmt.Errorf("transport closed")
}
msgBytes, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("pipeTransport: could not marshal json: %w", err)
}
if os.Getenv("DEBUGP") != "" {
fmt.Fprintf(os.Stdout, "\x1b[32mSEND>\x1b[0m\n%s\n", msgBytes)
}
lengthPadding := make([]byte, 4)
binary.LittleEndian.PutUint32(lengthPadding, uint32(len(msgBytes)))
if _, err = t.writer.Write(append(lengthPadding, msgBytes...)); err != nil {
return err
}
return nil
}
func (t *pipeTransport) Close() error {
select {
case <-t.closed:
return nil
default:
return t.onClose()
}
}
func (t *pipeTransport) isClosed() bool {
select {
case <-t.closed:
return true
default:
return false
}
}
func newPipeTransport(driver *PlaywrightDriver, stderr io.Writer) (transport, error) {
t := &pipeTransport{
closed: make(chan struct{}, 1),
}
cmd := driver.Command("run-driver")
cmd.Stderr = stderr
stdin, err := cmd.StdinPipe()
if err != nil {
return nil, fmt.Errorf("could not create stdin pipe: %w", err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("could not create stdout pipe: %w", err)
}
t.writer = stdin
t.bufReader = bufio.NewReader(stdout)
t.onClose = func() error {
select {
case <-t.closed:
default:
close(t.closed)
}
if err := t.writer.Close(); err != nil {
return err
}
// playwright-cli will exit when its stdin is closed
if err := cmd.Wait(); err != nil {
return err
}
return nil
}
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("could not start driver: %w", err)
}
return t, nil
}