browsed 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4631a9e5e0982106f635f4ac515e7cf4e25259df9cbf14b40526e30fa1737147
4
- data.tar.gz: 025df085a9ccdb79abb3282c20901831195e098e36182a2592dd62585e888f0f
3
+ metadata.gz: 1ac593ddda39f90bf445f8e858cd4a05a3b08cf997f33b1ee448f9791127148c
4
+ data.tar.gz: e79c1d0d006db49e81b872706868ca00d5ddc087b52738e19a863df33c598cf3
5
5
  SHA512:
6
- metadata.gz: b6802e8ba20979342205b7bd4a5a111cbca200269dcbc49521a17e53a6b4743a46901d2b601defb48bcc4b9364c62b74ade6ef7dd145f80ea9e2521190355697
7
- data.tar.gz: 988985ccb8ba4f478c7599e9d5d37a27e36b45446ffb82d13661d532d64a8b8869c6f16efb175e9d40dfaec8d9553116e2aa876f0656c648f54471c5b284d4ce
6
+ metadata.gz: 7bc94644afe6938c6df5ce3e3ad53a7c99b93acc52334fab62a1a03c0913551071a07aa3525ff400d97180bc508ac7cbdff645ca2c9e41ff66ba0913769240aa
7
+ data.tar.gz: 80db44c4e2bae419e34e5aba0c28d597a55d1204ce3d2c0cce1fbda40204bfd959fb0180e4b8b74a6284a4f5bd2fcad2d5536c7f24405018a6c5229e2f3c1dde
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- browsed (0.1.8)
4
+ browsed (0.1.9)
5
5
  agents
6
6
  capybara (~> 3.4, >= 3.4.1)
7
7
  poltergeist (~> 1.18, >= 1.18.1)
@@ -2,7 +2,7 @@ module Browsed
2
2
  module Chrome
3
3
 
4
4
  private
5
- def register_chrome_driver(driver_options: {}, timeout: 60, debug: false)
5
+ def register_chrome_driver(options: {}, timeout: 60, debug: false)
6
6
  profile = Selenium::WebDriver::Chrome::Profile.new
7
7
 
8
8
  profile["user-agent"] = self.user_agent unless self.user_agent.to_s.empty?
@@ -1,12 +1,10 @@
1
1
  module Browsed
2
2
  class Client
3
3
  attr_accessor :configuration
4
+ attr_accessor :driver, :browser, :browser_id, :environment
5
+ attr_accessor :session, :proxy
6
+ attr_accessor :device, :user_agent, :resolution
4
7
  attr_accessor :manager, :maximum_processes
5
- attr_accessor :driver, :browser, :environment
6
- attr_accessor :session
7
- attr_accessor :device, :proxy
8
- attr_accessor :user_agent
9
- attr_accessor :resolution
10
8
 
11
9
  include Capybara::DSL
12
10
 
@@ -18,7 +16,7 @@ module Browsed
18
16
  user_agent: nil,
19
17
  resolution: nil,
20
18
  environment: :production,
21
- driver_options: {},
19
+ options: {},
22
20
  maximum_processes: nil)
23
21
 
24
22
  self.configuration = configuration
@@ -27,34 +25,36 @@ module Browsed
27
25
  self.browser = browser || self.configuration.browser
28
26
  self.environment = environment || self.configuration.environment
29
27
 
28
+ self.browser_id = generate_browser_id
30
29
  self.device = device
31
30
  self.proxy = proxy
32
31
 
33
- self.manager = Browsed::Manager.new(browser: self.browser)
32
+ self.manager = ::Browsed::Manager.new(browser: self.browser)
34
33
  self.maximum_processes = maximum_processes || self.configuration.maximum_processes
35
34
 
36
35
  set_user_agent(user_agent)
37
36
  set_resolution(resolution)
38
37
 
39
- setup_capybara(driver_options: driver_options)
38
+ options.merge!(browser_id: self.browser_id)
39
+ setup_capybara(options: options)
40
40
  end
41
41
 
42
42
  include ::Browsed::Poltergeist
43
43
  include ::Browsed::Firefox
44
44
  include ::Browsed::Chrome
45
45
 
46
- def setup_capybara(driver_options: {}, retries: 3)
46
+ def setup_capybara(options: {}, retries: 3)
47
47
  if can_start_new_process?
48
- register_driver!(driver_options)
48
+ register_driver!(options)
49
49
 
50
50
  Capybara.default_driver = self.driver
51
51
  Capybara.javascript_driver = self.driver
52
52
 
53
- Capybara.default_max_wait_time = driver_options.fetch(:wait_time, 30) #seconds
53
+ Capybara.default_max_wait_time = options.fetch(:wait_time, 30) #seconds
54
54
 
55
55
  self.session = Capybara::Session.new(self.driver)
56
56
  else
57
- raise Browsed::TooManyProcessesError, "Too many PhantomJS processes running, reached maximum allowed number of #{self.maximum_processes}"
57
+ raise Browsed::TooManyProcessesError, "Too many #{self.browser} processes running, reached maximum allowed number of #{self.maximum_processes} processes."
58
58
  end
59
59
  end
60
60
 
@@ -78,25 +78,38 @@ module Browsed
78
78
  def reset_session!
79
79
  self.session.reset_session!
80
80
  end
81
+
82
+ def generate_browser_id
83
+ SecureRandom.hex[0..15]
84
+ end
81
85
 
82
- def terminate_session!
83
- self.session.reset_session!
84
- self.session.driver.quit
85
- self.session = nil
86
+ def quit!(retries: 3)
87
+ begin
88
+ self.session.driver.quit
89
+ rescue Exception
90
+ retries -= 1
91
+ retry if retries > 0
92
+ end
93
+
94
+ # If Selenium/Phantom somehow isn't able to shut down the browser, force a shutdown using kill -9
95
+ self.manager.set_command(browser_id: self.browser_id)
96
+ self.manager.kill_processes!
97
+
98
+ self.session = nil
86
99
  end
87
100
 
88
101
  private
89
- def register_driver!(driver_options = {})
102
+ def register_driver!(options = {})
90
103
  if poltergeist?
91
- register_poltergeist_driver(driver_options: driver_options)
104
+ register_poltergeist_driver(options: options)
92
105
  elsif selenium?
93
106
  if firefox_browser?
94
- register_firefox_driver(driver_options: driver_options)
107
+ register_firefox_driver(options: options)
95
108
  elsif firefox_headless_browser?
96
- register_firefox_driver(driver_options: driver_options.merge(headless: true))
109
+ register_firefox_driver(options: options.merge(headless: true))
97
110
  elsif chrome_browser?
98
111
  self.driver = :selenium_chrome
99
- register_chrome_driver(driver_options: driver_options)
112
+ register_chrome_driver(options: options)
100
113
  end
101
114
  end
102
115
  end
@@ -2,10 +2,11 @@ module Browsed
2
2
  module Firefox
3
3
 
4
4
  private
5
- def register_firefox_driver(driver_options: {}, timeout: 60, debug: false)
6
- headless = driver_options.fetch(:headless, false)
7
- download_path = driver_options.fetch(:download_path, self.configuration.download_path)
8
- private_browsing = driver_options.fetch(:private_browsing, false)
5
+ def register_firefox_driver(options: {}, timeout: 60, debug: false)
6
+ browser_id = options.fetch(:browser_id, nil)
7
+ headless = options.fetch(:headless, false)
8
+ download_path = options.fetch(:download_path, self.configuration.download_path)
9
+ private_browsing = options.fetch(:private_browsing, false)
9
10
 
10
11
  profile = Selenium::WebDriver::Firefox::Profile.new
11
12
 
@@ -22,15 +23,21 @@ module Browsed
22
23
  profile["pdfjs.disabled"] = true
23
24
  end
24
25
 
26
+ profile["browser.tabs.warnOnClose"] = false
27
+ profile["browser.tabs.warnOnCloseOtherTabs"] = false
25
28
  profile["general.useragent.override"] = self.user_agent unless self.user_agent.to_s.empty?
26
29
 
30
+ id = SecureRandom.hex[0..15]
27
31
  profile = firefox_proxy_options(profile)
28
32
  options = Selenium::WebDriver::Firefox::Options.new(profile: profile)
29
- options.args << "--headless" if headless
33
+ options.args << "--browser_id=#{id}" unless browser_id.to_s.empty?
34
+ options.args << "--headless" if headless
30
35
 
31
36
  Capybara.register_driver self.driver do |app|
32
37
  Capybara::Selenium::Driver.new(app, browser: :firefox, options: options)
33
38
  end
39
+
40
+ return id
34
41
  end
35
42
 
36
43
  def firefox_proxy_options(profile)
@@ -1,46 +1,35 @@
1
1
  module Browsed
2
2
  class Manager
3
- attr_accessor :command, :kill_signal
3
+ attr_accessor :command, :kill_signal, :logging
4
4
 
5
- def initialize(browser: :phantomjs, kill_signal: 9)
6
- case browser
7
- when :phantomjs
8
- self.command = "ps -ef | grep /[p]hantomjs"
9
- when :firefox
10
- self.command = "ps -ef | grep /[g]eckodriver"
11
- when :chrome
12
- self.command = "ps -ef | grep /[c]hromedriver"
13
- else
14
- self.command = "ps -ef | grep /[p]hantomjs"
15
- end
5
+ def initialize(command: nil, browser_id: nil, browser: :phantomjs, kill_signal: 9, logging: false)
6
+ set_command(command: command, browser_id: browser_id, browser: browser)
16
7
 
17
8
  self.kill_signal = kill_signal
9
+ self.logging = logging
18
10
  end
19
11
 
20
- def can_start_more_processes?(max_count: 5)
21
- return get_current_processes.size < max_count
22
- end
23
-
24
- def reap_stale_processes(started_after: ::Browsed.configuration.processes_max_ttl.call)
25
- processes = get_current_processes
26
-
27
- processes.each do |process|
28
- if (process[:date] && process[:date] < (Time.now - started_after))
29
- info "[Browsed::Manager] - #{Time.now.to_s(:db)}: Process with PID #{process[:pid]} was started before #{started_after.to_s(:db)}. Process should be terminated."
30
- kill_process(process)
12
+ def set_command(command: nil, browser_id: nil, browser: :phantomjs)
13
+ if !command.to_s.empty?
14
+ self.command = command
15
+ elsif !browser_id.to_s.empty?
16
+ self.command = "ps aux | awk '/--browser_id=#{browser_id}/'"
17
+ else
18
+ case browser
19
+ when :phantomjs
20
+ self.command = "ps -ef | grep /[p]hantomjs"
21
+ when :firefox
22
+ self.command = "ps -ef | grep /[f]irefox-bin"
23
+ when :chrome
24
+ self.command = "ps -ef | grep /[c]hromedriver"
25
+ else
26
+ self.command = "ps -ef | grep /[p]hantomjs"
31
27
  end
32
- end if processes && processes.any?
28
+ end
33
29
  end
34
30
 
35
- def kill_process(process)
36
- info "[Browsed::Manager] - #{Time.now.to_s(:db)}: Killing process with PID #{process[:pid]} matching command #{self.command}."
37
-
38
- begin
39
- ::Process.kill(self.kill_signal, process[:pid])
40
-
41
- rescue StandardError => e
42
- info "[Browsed::Manager] - #{Time.now.to_s(:db)}: Failed to kill process with pid '#{process[:pid]}'. Error Class: #{e.class.name}. Error Message: #{e.message}"
43
- end
31
+ def can_start_more_processes?(max_count: nil)
32
+ return max_count.nil? || get_current_processes.size < max_count
44
33
  end
45
34
 
46
35
  def get_current_processes
@@ -55,44 +44,63 @@ module Browsed
55
44
  return processes
56
45
  end
57
46
 
58
- private
47
+ def kill_processes!(started_after: nil)
48
+ processes = get_current_processes
49
+
50
+ processes.each do |process|
51
+ killable = started_after.nil? || (process[:date] && process[:date] < (Time.now - started_after))
52
+ kill_process!(process) if killable
53
+ end if processes && processes.any?
54
+ end
59
55
 
60
- def parse_process(process_data)
61
- process = {}
56
+ def kill_process!(process)
57
+ info "[Browsed::Manager] - #{Time.now.to_s}: Killing process with PID #{process[:pid]} matching command #{self.command}."
62
58
 
63
- parts = process_data.split(' ')
64
- pid = parts[1].to_i
65
- started = parts[4].to_s
66
- date = parse_date(started)
59
+ begin
60
+ ::Process.kill(self.kill_signal, process[:pid])
61
+
62
+ rescue StandardError => e
63
+ info "[Browsed::Manager] - #{Time.now.to_s}: Failed to kill process with pid '#{process[:pid]}'. Error Class: #{e.class.name}. Error Message: #{e.message}"
64
+ end
65
+ end
66
+
67
+ private
68
+ def parse_process(process_data)
69
+ process = {}
67
70
 
68
- process[:pid] = pid
69
- process[:started] = started
70
- process[:date] = date
71
+ parts = process_data.split(' ')
72
+ pid = parts[1].to_i
73
+ started = parts[4].to_s
74
+ date = parse_date(started)
71
75
 
72
- info "[Browsed::Manager] - #{Time.now.to_s(:db)}: Pid: #{pid}. Started: #{started}. Date: #{date}.\n"
76
+ process[:pid] = pid
77
+ process[:started] = started
78
+ process[:date] = date
73
79
 
74
- return process
75
- end
80
+ info "[Browsed::Manager] - #{Time.now.to_s}: Pid: #{pid}. Started: #{started}. Date: #{date}.\n"
81
+
82
+ return process
83
+ end
76
84
 
77
- def parse_date(date, retries = 3)
78
- begin
79
- if (!(date =~ /^[a-z]{3,4}\d*/i).nil?) #Sep16
80
- parsed_date = DateTime.strptime(date, "%b%d")
85
+ def parse_date(date, retries = 3)
86
+ begin
87
+ if (!(date =~ /^[a-z]{3,4}\d*/i).nil?) #Sep16
88
+ parsed_date = DateTime.strptime(date, "%b%d")
81
89
 
82
- elsif (!(date =~ /^\d*:\d*/i).nil?) #11:34
83
- parsed_date = Time.strptime(date, "%H:%M").to_datetime
84
- end
90
+ elsif (!(date =~ /^\d*:\d*/i).nil?) #11:34
91
+ parsed_date = Time.strptime(date, "%H:%M").to_datetime
92
+ end
85
93
 
86
- rescue StandardError => e
87
- info "[Browsed::Manager] - #{Time.now.to_s(:db)}: Exception occurred while trying to parse date/time string '#{date}'. Error Class: #{e.class.name}. Error: #{e.message}."
88
- retries -= 1
89
- retry if retries > 0
94
+ rescue StandardError => e
95
+ info "[Browsed::Manager] - #{Time.now.to_s}: Exception occurred while trying to parse date/time string '#{date}'. Error Class: #{e.class.name}. Error: #{e.message}."
96
+ retries -= 1
97
+ retry if retries > 0
98
+ end
90
99
  end
91
- end
92
100
 
93
- def info(message, enabled = true)
94
- puts message if enabled
95
- end
101
+ def info(message)
102
+ puts message if self.logging
103
+ end
96
104
 
97
105
  end
98
106
  end
@@ -2,9 +2,9 @@ module Browsed
2
2
  module Poltergeist
3
3
 
4
4
  private
5
- def register_poltergeist_driver(driver_options: {}, timeout: 60, debug: false)
5
+ def register_poltergeist_driver(options: {}, timeout: 60, debug: false)
6
6
  phantom_opts = ['--ignore-ssl-errors=true', '--ssl-protocol=any']
7
- disable_images = driver_options.fetch(:disable_images, false)
7
+ disable_images = options.fetch(:disable_images, false)
8
8
 
9
9
  if disable_images
10
10
  phantom_opts << "--load-images=false"
@@ -1,3 +1,3 @@
1
1
  module Browsed
2
- VERSION = "0.1.8"
2
+ VERSION = "0.1.9"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: browsed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-08-26 00:00:00.000000000 Z
11
+ date: 2018-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara