class Playwright::Transport

ref: github.com/microsoft/playwright-python/blob/master/playwright/_impl/_transport.py

Public Class Methods

new(playwright_cli_executable_path:) click to toggle source

@param playwright_cli_executable_path [String] path to playwright-cli.

# File lib/playwright/transport.rb, line 11
def initialize(playwright_cli_executable_path:)
  @driver_executable_path = playwright_cli_executable_path
  @debug = ENV['DEBUG'].to_s == 'true' || ENV['DEBUG'].to_s == '1'
  @mutex = Mutex.new
end

Public Instance Methods

async_run() click to toggle source

Start `playwright-cli run-driver`

@note This method blocks until playwright-cli exited. Consider using Thread or Future.

# File lib/playwright/transport.rb, line 48
def async_run
  @stdin, @stdout, @stderr, @thread = Open3.popen3("#{@driver_executable_path} run-driver")
  @stdin.binmode  # Ensure Strings are written 1:1 without encoding conversion, necessary for integer values

  Thread.new { handle_stdout }
  Thread.new { handle_stderr }
end
on_driver_crashed(&block) click to toggle source
# File lib/playwright/transport.rb, line 21
def on_driver_crashed(&block)
  @on_driver_crashed = block
end
on_message_received(&block) click to toggle source
# File lib/playwright/transport.rb, line 17
def on_message_received(&block)
  @on_message = block
end
send_message(message) click to toggle source

@param message [Hash]

# File lib/playwright/transport.rb, line 28
def send_message(message)
  debug_send_message(message) if @debug
  msg = JSON.dump(message)
  @mutex.synchronize {
    @stdin.write([msg.bytes.length].pack('V')) # unsigned 32bit, little endian, real byte size instead of chars
    @stdin.write(msg) # write UTF-8 in binary mode as byte stream
  }
rescue Errno::EPIPE, IOError
  raise AlreadyDisconnectedError.new('send_message failed')
end
stop() click to toggle source

Terminate playwright-cli driver.

# File lib/playwright/transport.rb, line 40
def stop
  [@stdin, @stdout, @stderr].each { |io| io.close unless io.closed? }
  @thread&.terminate
end

Private Instance Methods

debug_recv_message(message) click to toggle source
# File lib/playwright/transport.rb, line 103
def debug_recv_message(message)
  puts "\x1b[33mRECV>\x1b[0m#{message}"
end
debug_send_message(message) click to toggle source
# File lib/playwright/transport.rb, line 99
def debug_send_message(message)
  puts "\x1b[33mSEND>\x1b[0m#{message}"
end
handle_stderr() click to toggle source
# File lib/playwright/transport.rb, line 76
def handle_stderr
  while err = @stderr.read
    # sometimed driver crashes with the error below.
    # --------
    # undefined:1
    # �
    # ^

    # SyntaxError: Unexpected token � in JSON at position 0
    #     at JSON.parse (<anonymous>)
    #     at Transport.transport.onmessage (/home/runner/work/playwright-ruby-client/playwright-ruby-client/node_modules/playwright/lib/cli/driver.js:42:73)
    #     at Immediate.<anonymous> (/home/runner/work/playwright-ruby-client/playwright-ruby-client/node_modules/playwright/lib/protocol/transport.js:74:26)
    #     at processImmediate (internal/timers.js:461:21)
    if err.include?('undefined:1')
      @on_driver_crashed&.call
      break
    end
    $stderr.write(err)
  end
rescue IOError
  # disconnected by remote.
end
handle_stdout(packet_size: 32_768) click to toggle source
# File lib/playwright/transport.rb, line 58
def handle_stdout(packet_size: 32_768)
  while chunk = @stdout.read(4)
    length = chunk.unpack1('V') # unsigned 32bit, little endian
    buffer = StringIO.new
    (length / packet_size).to_i.times do
      buffer << @stdout.read(packet_size)
    end
    buffer << @stdout.read(length % packet_size)
    buffer.rewind
    obj = JSON.parse(buffer.read)

    debug_recv_message(obj) if @debug
    @on_message&.call(obj)
  end
rescue IOError
  # disconnected by remote.
end