max pleaner max pleaner - 4 months ago 24
Ruby Question

Headless operations don't work inside Sinatra route

I am using the

headless
and
selenium-webdriver
gems to launch a headless Firefox browser:

headless = Headless.new(
video: {
frame_rate: 12,
codec: 'libx264'
}
)
headless.start
driver = Selenium::WebDriver.for(:firefox)


With this code I can write the following:

driver.navigate.to("http://google.com")


Yet the following raises an error after I visit '/' in the browser:

get '/' do
driver.navigate.to("http://google.com")
erb :root
end


The error is as follows:

*** Errno::ECONNREFUSED Exception: Failed to open TCP connection to 127.0.0.1:7055 (Connection refused - connect(2) for "127.0.0.1" port 7055)


I'm pretty sure this is because the
driver.navigate
is not being called in the headless scope, therefore it can't connect to the Firefox instance.

I have also tried using the modular sinatra style, but the same error occurred.




workaround

What I ended up doing is separating the headless server in a separate script. This script has
loop
and gets input, printing the output of running the command in the headless scope. Then from the sinatra server, i use
PTY.spawn
to instantiate the server and pass around its stdin and stdout so I can use it in my Sinatra app. This way the headless script is only run once (therefore multiple headless servers aren't started) and I can connect to it from my Sinatra routes. I can't interact with the headless script's variables or methods directly - I need to just use i/o.

I am hoping for an answer which hows how to make the original code work, though (when the sinatra app's routes are called in headless scope)

Answer

One possible trick that can help:

this = self
get '/' do
  this.driver.navigate.to("http://google.com")
  erb :root
end

What is really important, the headless browser is to be started after the sinatra route was hit. So putting start_headless and start_driver methods in a separate class and invoking them when appropriate would do a trick.