browsed 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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