class Arachni::BrowserCluster::Worker
Overrides some {Arachni::Browser} methods to make multiple browsers play well with each other when they’re part of a {BrowserCluster}.
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Constants
- TRIES
How many times to retry timed-out jobs.
Attributes
job[R]
@return [Job]
Currently assigned job.
master[R]
@return [BrowserCluster]
max_time_to_live[RW]
@return [Integer]
time_to_live[R]
@return [Integer]
Remaining time-to-live measured in jobs.
Public Class Methods
new( options = {} )
click to toggle source
Calls superclass method
Arachni::Browser::new
# File lib/arachni/browser_cluster/worker.rb, line 40 def initialize( options = {} ) @master = options.delete( :master ) @max_time_to_live = options.delete( :max_time_to_live ) || Options.browser_cluster.worker_time_to_live @time_to_live = @max_time_to_live @done_signal = Queue.new # Don't store pages if there's a master, we'll be sending them to him # as soon as they're logged. super options.merge( store_pages: false ) start_capture return if !@master start end
Public Instance Methods
distribute_event( resource, element, event )
click to toggle source
Direct the distribution to the master and let it take it from there.
@see Jobs::EventTrigger @see BrowserCluster#queue
# File lib/arachni/browser_cluster/worker.rb, line 122 def distribute_event( resource, element, event ) master.queue( @job.forward_as( @job.class::EventTrigger, resource: resource, element: element, event: event ), master.callback_for( @job ) ) true # Job may have been marked as done or the cluster may have been shut down. rescue BrowserCluster::Job::Error::AlreadyDone, BrowserCluster::Error::AlreadyShutdown false end
inspect()
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 183 def inspect s = "#<#{self.class} " s << "pid=#{@lifeline_pid} " s << "job=#{@job.inspect} " s << "last-url=#{@last_url.inspect} " s << "transitions=#{@transitions.size}" s << '>' end
run_job( job )
click to toggle source
@param [BrowserCluster::Job] job
@return [Array<Page>]
Pages which resulted from firing events, clicking JavaScript links and capturing AJAX requests.
@see Arachni::Browser#trigger_events
# File lib/arachni/browser_cluster/worker.rb, line 66 def run_job( job ) @job = job print_debug "Started: #{@job}" # PhantomJS may have crashed (it happens sometimes) so make sure that # we've got a live one before running the job. # If we can't respawn, then bail out. return if browser_respawn_if_necessary.nil? time = Time.now begin Timeout.timeout Options.browser_cluster.job_timeout do @job.configure_and_run( self ) end @job.time = Time.now - time rescue Selenium::WebDriver::Error::WebDriverError, Watir::Exception::Error => e print_debug "WebDriver error while processing job: #{@job}" print_debug_exception e # This can be thrown by a Selenium call somewhere down the line, # catch it here and retry the entire job. rescue Timeout::Error => e @job.timed_out!( Time.now - time ) print_bad "Job timed-out: #{@job}" print_debug_exception e master.increment_time_out_count end decrease_time_to_live # Something went horribly wrong, cleanup. rescue => e print_error "Error while processing job: #{@job}" print_exception e browser_respawn ensure print_debug "Finished: #{@job}" @job = nil reset master.job_done job end
shutdown( wait = true )
click to toggle source
@note If there is a running job it will wait for it to finish.
Shuts down the worker.
# File lib/arachni/browser_cluster/worker.rb, line 143 def shutdown( wait = true ) return if @shutdown @shutdown = true print_debug "Shutting down (wait: #{wait}) ..." # Keep checking to see if any of the 'done' criteria are true. kill_check = Thread.new do while wait && @job print_debug_level_2 "Waiting for job to complete: #{job}" sleep 0.1 end print_debug_level_2 'Signaling done.' @done_signal << nil end print_debug_level_2 'Waiting for done signal...' # If we've got a job running wait for it to finish before closing the # browser otherwise we'll get Selenium errors and zombie processes. @done_signal.pop print_debug_level_2 '...done.' print_debug_level_2 'Waiting for kill check...' kill_check.join print_debug_level_2 '...done.' if @consumer print_debug_level_2 'Killing consumer thread...' @consumer.kill print_debug_level_2 '...done.' end print_debug_level_2 'Calling parent shutdown...' browser_shutdown print_debug_level_2 '...done.' print_debug '...shutdown complete.' end
Also aliased as: browser_shutdown
Private Instance Methods
browser_respawn()
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 243 def browser_respawn print_debug "Re-spawning browser (TTD?: #{time_to_die?}) ..." @time_to_live = @max_time_to_live browser_shutdown # Browser may fail to respawn but there's nothing we can do about that, # just leave it dead and try again at the next job. r = begin start_webdriver true rescue Selenium::WebDriver::Error::WebDriverError, Browser::Error::Spawn => e print_error 'Could not respawn the browser, will try again at' << " the next job. (#{e})" print_error 'Please try increasing the maximum open files limit' << ' of your OS.' nil end print_debug "...re-spawned browser: #{r}" r end
browser_respawn_if_necessary()
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 238 def browser_respawn_if_necessary return false if !time_to_die? browser_respawn end
decrease_time_to_live()
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 275 def decrease_time_to_live @time_to_live -= 1 end
reset()
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 194 def reset @javascript.taint = nil clear_buffers # The jobs may have configured callbacks to capture pages etc., # remove them. clear_observers end
skip_state( state )
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 218 def skip_state( state ) master.skip_state job.id, state end
skip_state?( state )
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 214 def skip_state?( state ) master.skip_state? job.id, state end
skip_states()
click to toggle source
@return [Support::LookUp::HashSet]
States that have been visited and should be skipped, for the given {#job}.
@see skip_state
@see skip_state?
# File lib/arachni/browser_cluster/worker.rb, line 210 def skip_states master.skip_states job.id end
start()
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 226 def start @consumer ||= Thread.new do while !@shutdown run_job master.pop end print_debug 'Got shutdown signal...' @done_signal << nil print_debug '...and acknowledged it.' end end
time_to_die?()
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 271 def time_to_die? @time_to_live <= 0 end
update_skip_states( states )
click to toggle source
# File lib/arachni/browser_cluster/worker.rb, line 222 def update_skip_states( states ) master.update_skip_states job.id, states end