module Tor
Constants
- MOBILE_AGENT
- USER_AGENT
- VERSION
Public Class Methods
config()
click to toggle source
# File lib/rest-tor.rb, line 25 def self.config @config ||= Configuration.new end
logger()
click to toggle source
# File lib/rest-tor.rb, line 9 def self.logger @logger ||= Logger.new(STDOUT).tap do |log| log.formatter = proc do |severity, datetime, progname, msg| "[#{datetime.strftime('%Y-%m-%d %H:%M:%S.%3N')}] [#{Process.pid}-#{Thread.current.object_id}] #{msg}\n" end end end
logger=(log)
click to toggle source
# File lib/rest-tor.rb, line 17 def self.logger=(log) @logger = log end
setup(&block)
click to toggle source
# File lib/rest-tor.rb, line 21 def self.setup(&block) instance_exec(&block) end
Public Instance Methods
avaliable?()
click to toggle source
# File lib/rest_tor/instance.rb, line 96 def avaliable? begin pid && Process.getpgid( pid ) && true rescue Errno::ESRCH false end end
clear()
click to toggle source
# File lib/rest_tor/tor.rb, line 161 def clear Dir.glob(config.dir.join("**/*.pid")).each do |path| begin Process.kill("KILL", File.read(path).chomp.to_i) rescue Errno::ESRCH end end FileUtils.rm_rf(config.dir) true ensure store.clear end
count()
click to toggle source
# File lib/rest_tor/tor.rb, line 174 def count Dir.glob(config.dir.join("*")).count end
dir(port)
click to toggle source
# File lib/rest_tor/tor.rb, line 143 def dir(port) config.dir.join("#{port}").tap do |dir| FileUtils.mkpath(dir) if not Dir.exists?(dir) end end
hold_tor(mode: :default, rest: false) { || ... }
click to toggle source
# File lib/rest_tor/tor.rb, line 24 def hold_tor(mode: :default, rest: false, &block) return yield if rest if tor=Thread.current[:tor] port = tor.port else port, tor = Dispatcher.take(mode: mode) end Thread.current[:tor] = tor if block_given? yield port, tor end ensure Thread.current[:tor] = nil tor && tor.release! end
init()
click to toggle source
# File lib/rest_tor/tor.rb, line 12 def init lock("tor:init", expires: 1.minutes) do threads = [] config.count.times { |i| threads << Thread.new { listen(config.port + i + 1) } } threads.map(&:join) end end
listen(port)
click to toggle source
# File lib/rest_tor/tor.rb, line 116 def listen(port) return if port.blank? || !port.to_s.match(/^\d+$/) logger.info "Open tor with port:#{port}" system config.command.call(port) if ip=test(port) store.insert(port, ip) else tor = Tor.store[port] raise DiedPortError if tor&.died? raise UnvaliablePort if !tor end rescue Error, RestClient::Exception => e stop(port) logger.info "#{e.class}:#{e.message}" retry end
request(options={}) { |res, req, headers| ... }
click to toggle source
# File lib/rest_tor/tor.rb, line 42 def request(options={}, &block) url = options[:url] mobile = options[:mobile] proxy = options[:proxy] proxy = nil if proxy == true raw = options[:raw].nil? ? true : false mode = options[:mode] || :default method = options[:method] || :get payload = options[:payload] || {} timeout = options[:timeout] || 10 format = options[:format] || (raw ? :html : :string) headers = options[:headers] || options[:header] || {} default_header = { 'User-Agent' => mobile ? MOBILE_AGENT : USER_AGENT } time, body = Time.now, nil rest = proxy != nil hold_tor(mode: mode, rest: rest) do |port, tor| Thread.current[:tor] = tor if tor.present? proxy ||= "socks5://127.0.0.1:#{port}" if not rest logger.info "Started #{method.to_s.upcase} #{url.inspect} (proxy:#{proxy} | mode:#{mode})" params = { method: method, url: url, payload: payload, proxy: proxy, timeout: timeout, headers: default_header.merge(headers) } begin response = RestClient::Request.execute(params) do |res, req, headers| if res.code == 302 res.follow_redirection else yield(res, req, headers ) if block_given? res end end tor&.success! body = response.body logger.info "Completed #{response.try(:code)} OK in #{(Time.now-time).round(1)}s (size: #{Utils.number_to_human_size(body.bytesize)})" rescue Exception => e if tor tor.fail!(e) logger.info "#{e.class}: #{e.message}, <Tor#(success: #{tor.counter.success}, fail: #{tor.counter.fail}, port: #{tor.port})>" end raise e end end case format.to_s when "html" then Nokogiri::HTML(Utils.encode_html(body)) when "json" then Utils.to_json(body) when "string" then body else raise InvalidFormat, format.to_s end end
restart(port)
click to toggle source
# File lib/rest_tor/tor.rb, line 136 def restart(port) lock("tor:#{port}:restart", expires: 1.minutes) do stop(port) listen(port) end end
stop(port)
click to toggle source
# File lib/rest_tor/tor.rb, line 103 def stop(port) logger.info "Stop tor port:#{port}" instance = store[port] if instance && instance.pid Process.kill("KILL", instance.pid) end FileUtils.rm_rf dir(port) rescue Exception ensure store.delete(port) end
store()
click to toggle source
# File lib/rest_tor/tor.rb, line 20 def store @store ||= Redis::HashKey.new('tor', marshal: true).tap { |s| s.send(:extend, Builder) } end
test(port)
click to toggle source
# File lib/rest_tor/tor.rb, line 149 def test(port) logger.info "Testing tor #{port}" req = RestClient::Request.execute({method: :get, url: config.ipApi, proxy: "socks5://127.0.0.1:#{port}"}) config.ipParser.call(req.body).tap do |ip| logger.info " IP: #{ip} " end rescue Net::OpenTimeout, Net::ReadTimeout, Errno::ECONNREFUSED, SOCKSError, SOCKSError::TTLExpired, Errno::ECONNRESET => e logger.error "#{e.class}: #{e.message}" false end
unused()
click to toggle source
# File lib/rest_tor/tor.rb, line 178 def unused store.select {|_, options| !options.using? } end