rubycdp / ferrum

Headless Chrome Ruby API
https://ferrum.rubycdp.com
MIT License
1.7k stars 122 forks source link

Is it possible to listen socket?(question) #312

Closed dunaevv closed 1 year ago

dunaevv commented 1 year ago

I have Python code(Playwright):

...
page = browser.new_page()
page.on("websocket", on_web_socket)

def on_web_socket(ws):
    """Listen socket"""
    print(f"[*] WebSocket opened: {ws.url}")
    # ws.on("framesent", lambda payload: print(payload))
    ws.on("framereceived", on_data)
    # ws.on("close", lambda payload: print("WebSocket closed"))

So, I can go to source and listen socket, can I do the same with Ferrum? Thanks in advance!

ttilberg commented 1 year ago

It's pretty similar, but came into something unexpected I want to show to show @route for feedback.

You can leverage the on callbacks with the Network.webSocketFrameReceived and Network.webSocketFrameSent network events.

browser.on('Network.webSocketFrameReceived') do |msg|
  p msg
end

Complete example:

I set up a web app that loops websocket send/recvs, and this example script demonstrates how to intercept these messages:

require 'ferrum'
browser = Ferrum::Browser.new(headless: false)

browser.network.intercept

# We must allow requests to continue since they are being intercepted.
# I'm not sure if this is still required if we can figure out
#   browser.network.intercept(resource_type: :WebSocket)
#
browser.on(:request) do |request|
  request.continue
end

# Using browser.on, you can hook into the network events found here:
#   https://chromedevtools.github.io/devtools-protocol/tot/Network/
#
browser.on('Network.webSocketFrameReceived') do |msg|
  puts '◀ ' * 30
  puts "◀ Received Websocket Stuff:"
  p msg
  puts '◀ ' * 30
  puts "\n"
end

browser.on('Network.webSocketFrameSent') do |msg|
  puts '▶ ' * 30
  puts "▶ Sent Websocket Stuff:"
  p msg
  puts '▶ ' * 30
  puts "\n"
end

browser.goto 'http://localhost:3000'

loop do
  sleep 1.0
end
image

Quick detour:

Network Intercept resource type -> Fetch.enable pattern WebSocket invalid?

@route (or someone) Can you check this and let me know if you think I've misunderstood something, or if something is amiss?

Given Request Patterns Allowed Resource Types

It seems like we should be able to register interception of only websockets by doing:

browser.network.intercept(content_type: "WebSocket")
# From Ferrum::Network:

    def intercept(pattern: "*", resource_type: nil)
      pattern = { urlPattern: pattern }
      pattern[:resourceType] = resource_type if resource_type && RESOURCE_TYPES.include?(resource_type.to_s)

      @page.command("Fetch.enable", handleAuthRequests: true, patterns: [pattern])
    end

I get the following error from CDP:

Unknown resource type in fetch filter: 'WebSocket' (Ferrum::BrowserError)

▶ 6.333580000326037 {"method":"Fetch.enable","params":{"handleAuthRequests":true,"patterns":[{"urlPattern":"*","resourceType":"WebSocket"}]},"id":1011}
    ◀ 6.334255000576377 {"id":1011,"error":{"code":-32602,"message":"Unknown resource type in fetch filter: 'WebSocket'"}}
/Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/browser/client.rb:97:in `raise_browser_error': Unknown resource type in fetch filter: 'WebSocket' (Ferrum::BrowserError)
    from /Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/browser/client.rb:49:in `command'
    from /Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/page.rb:177:in `command'
    from /Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/network.rb:98:in `intercept'
    from browse.rb:4:in `<main>'

Think there's something funny here? I can't find much info (and don't know how to really look it up) on leveraging a WebSocket filter.

route commented 1 year ago

Beside WebSocket I've got the same result for TextTrack Prefetch EventSource Manifest SignedExchange and Preflight. So I believe it's Chrome's issue in the docs or in the implementation. Opened an issue https://bugs.chromium.org/p/chromium/issues/detail?id=1393503

And thanks for your solution! I think we can close this issue.

dunaevv commented 1 year ago

It's pretty similar, but came into something unexpected I want to show to show @route for feedback.

You can leverage the on callbacks with the Network.webSocketFrameReceived and Network.webSocketFrameSent network events.

browser.on('Network.webSocketFrameReceived') do |msg|
  p msg
end

Complete example:

I set up a web app that loops websocket send/recvs, and this example script demonstrates how to intercept these messages:

require 'ferrum'
browser = Ferrum::Browser.new(headless: false)

browser.network.intercept

# We must allow requests to continue since they are being intercepted.
# I'm not sure if this is still required if we can figure out
#   browser.network.intercept(resource_type: :WebSocket)
#
browser.on(:request) do |request|
  request.continue
end

# Using browser.on, you can hook into the network events found here:
#   https://chromedevtools.github.io/devtools-protocol/tot/Network/
#
browser.on('Network.webSocketFrameReceived') do |msg|
  puts '◀ ' * 30
  puts "◀ Received Websocket Stuff:"
  p msg
  puts '◀ ' * 30
  puts "\n"
end

browser.on('Network.webSocketFrameSent') do |msg|
  puts '▶ ' * 30
  puts "▶ Sent Websocket Stuff:"
  p msg
  puts '▶ ' * 30
  puts "\n"
end

browser.goto 'http://localhost:3000'

loop do
  sleep 1.0
end
image

Quick detour:

Network Intercept resource type -> Fetch.enable pattern WebSocket invalid?

@route (or someone) Can you check this and let me know if you think I've misunderstood something, or if something is amiss?

Given Request Patterns Allowed Resource Types

It seems like we should be able to register interception of only websockets by doing:

browser.network.intercept(content_type: "WebSocket")
# From Ferrum::Network:

    def intercept(pattern: "*", resource_type: nil)
      pattern = { urlPattern: pattern }
      pattern[:resourceType] = resource_type if resource_type && RESOURCE_TYPES.include?(resource_type.to_s)

      @page.command("Fetch.enable", handleAuthRequests: true, patterns: [pattern])
    end

I get the following error from CDP:

Unknown resource type in fetch filter: 'WebSocket' (Ferrum::BrowserError)

▶ 6.333580000326037 {"method":"Fetch.enable","params":{"handleAuthRequests":true,"patterns":[{"urlPattern":"*","resourceType":"WebSocket"}]},"id":1011}
    ◀ 6.334255000576377 {"id":1011,"error":{"code":-32602,"message":"Unknown resource type in fetch filter: 'WebSocket'"}}
/Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/browser/client.rb:97:in `raise_browser_error': Unknown resource type in fetch filter: 'WebSocket' (Ferrum::BrowserError)
  from /Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/browser/client.rb:49:in `command'
  from /Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/page.rb:177:in `command'
  from /Users/ttilberg/.asdf/installs/ruby/3.1.2/lib/ruby/gems/3.1.0/gems/ferrum-0.12/lib/ferrum/network.rb:98:in `intercept'
  from browse.rb:4:in `<main>'

Think there's something funny here? I can't find much info (and don't know how to really look it up) on leveraging a WebSocket filter.

Awesome! Thank u sir!)))