proxied 0.1.0

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.
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "proxied"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.config.history.file = File.join(__FILE__, "../.pry_history")
12
+ Pry.start
13
+
14
+ #require "irb"
15
+ #IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 5.2"
6
+ gem "activerecord", ">= 5.2", require: "active_record"
7
+ gem "actionpack", ">= 5.2"
8
+ gem "activemodel", ">= 5.2"
9
+ gem "railties", ">= 5.2"
10
+
11
+ gemspec path: "../"
@@ -0,0 +1,9 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 5.2"
6
+ gem "mongoid", "~> 7", require: "mongoid"
7
+ gem "bson_ext", platform: "ruby"
8
+
9
+ gemspec path: "../"
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/active_record"
4
+ require "generators/proxied/orm_helpers"
5
+
6
+ module ActiveRecord
7
+ module Generators
8
+ class ProxiedGenerator < ActiveRecord::Generators::Base
9
+ class_option :primary_key_type, type: :string, desc: "The type for primary key"
10
+
11
+ include Proxied::Generators::OrmHelpers
12
+ source_root File.expand_path("../templates", __FILE__)
13
+
14
+ def copy_proxy_migration
15
+ migration_template "migration.rb", "#{migration_path}/proxied_create_#{table_name}.rb", migration_version: migration_version
16
+ end
17
+
18
+ def generate_model
19
+ invoke "active_record:model", [name], migration: false unless model_exists? && behavior == :invoke
20
+ end
21
+
22
+ def inject_proxied_content
23
+ content = model_contents
24
+
25
+ class_path = if namespaced?
26
+ class_name.to_s.split("::")
27
+ else
28
+ [class_name]
29
+ end
30
+
31
+ indent_depth = class_path.size - 1
32
+ content = content.split("\n").map { |line| " " * indent_depth + line } .join("\n") << "\n"
33
+
34
+ inject_into_class(model_path, class_path.last, content) if model_exists?
35
+ end
36
+
37
+ def model_contents
38
+ <<RUBY
39
+ include ::Proxied::Sql::ProxyMethods
40
+ RUBY
41
+ end
42
+
43
+ def rails5?
44
+ Rails.version.start_with? '5'
45
+ end
46
+
47
+ def postgresql?
48
+ config = ActiveRecord::Base.configurations[Rails.env]
49
+ config && config['adapter'] == 'postgresql'
50
+ end
51
+
52
+ def migration_version
53
+ if rails5?
54
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
55
+ end
56
+ end
57
+
58
+ def primary_key_type
59
+ primary_key_string if rails5?
60
+ end
61
+
62
+ def primary_key_string
63
+ key_string = options[:primary_key_type]
64
+ ", id: :#{key_string}" if key_string
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ProxiedCreate<%= table_name.camelize %> < ActiveRecord::Migration<%= migration_version %>
4
+ def change
5
+ create_table :<%= table_name %><%= primary_key_type %> do |t|
6
+ t.string :host, null: false
7
+ t.integer :port, null: false
8
+ t.string :username
9
+ t.string :password
10
+
11
+ t.string :protocol, null: false, default: 'http', index: true
12
+ t.string :proxy_type, null: false, default: 'public', index: true
13
+ t.string :category
14
+
15
+ t.datetime :last_checked_at, index: true
16
+
17
+ t.boolean :valid_proxy, null: false, default: false, index: true
18
+ t.integer :successful_attempts, null: false, default: 0, index: true
19
+ t.integer :failed_attempts, null: false, default: 0, index: true
20
+ end
21
+
22
+ add_index :<%= table_name %>, [:host, :port], unique: true, name: 'index_unique_<%= table_name.singularize %>'
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+ require "generators/proxied/orm_helpers"
5
+
6
+ module Mongoid
7
+ module Generators
8
+ class ProxiedGenerator < Rails::Generators::NamedBase
9
+ include Proxied::Generators::OrmHelpers
10
+
11
+ def generate_model
12
+ invoke "mongoid:model", [name] unless model_exists? && behavior == :invoke
13
+ end
14
+
15
+ def inject_proxied_content
16
+ inject_into_file model_path, model_contents, after: inject_after_pattern(:mongoid) if model_exists?
17
+ end
18
+
19
+ def model_contents
20
+ <<RUBY
21
+ include Mongoid::Timestamps
22
+
23
+ # Fields
24
+ field :host, type: String
25
+ field :port, type: Integer
26
+
27
+ field :username, type: String
28
+ field :password, type: String
29
+
30
+ field :protocol, type: String, default: :http
31
+ field :proxy_type, type: String, default: :public
32
+ field :category, type: String
33
+
34
+ field :valid_proxy, type: Boolean, default: false
35
+ field :successful_attempts, type: Integer, default: 0
36
+ field :failed_attempts, type: Integer, default: 0
37
+
38
+ field :last_checked_at, type: DateTime
39
+
40
+ # Validations
41
+ validates :host, presence: true, uniqueness: {scope: :port}
42
+
43
+ # Indexes
44
+ index({ host: 1, port: 1 }, { unique: true, drop_dups: true })
45
+ index({ protocol: 1, proxy_type: 1, valid_proxy: -1, failed_attempts: 1 })
46
+ index({ valid_proxy: 1 })
47
+
48
+ # Methods
49
+ include ::Proxied::Nosql::ProxyMethods
50
+ RUBY
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module Proxied
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::ResourceHelpers
9
+
10
+ source_root File.expand_path("../../templates", __FILE__)
11
+
12
+ desc "Creates a Proxied initializer for your application."
13
+
14
+ def copy_initializer
15
+ template "proxied.rb", "config/initializers/proxied.rb"
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Proxied
4
+ module Generators
5
+ module OrmHelpers
6
+
7
+ private
8
+
9
+ def inject_after_pattern(orm = :active_record)
10
+ if orm == :active_record
11
+ /class #{table_name.camelize}\n|class #{table_name.camelize} .*\n|class #{table_name.demodulize.camelize}\n|class #{table_name.demodulize.camelize} .*\n/
12
+ else
13
+ /include Mongoid::Document\n|include Mongoid::Document .*\n/
14
+ end
15
+ end
16
+
17
+ def model_exists?
18
+ File.exist?(File.join(destination_root, model_path))
19
+ end
20
+
21
+ def migration_path
22
+ if Rails.version >= '5.0.3'
23
+ db_migrate_path
24
+ else
25
+ @migration_path ||= File.join("db", "migrate")
26
+ end
27
+ end
28
+
29
+ def model_path
30
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+
5
+ module Proxied
6
+ module Generators
7
+ class ProxiedGenerator < Rails::Generators::NamedBase
8
+ include Rails::Generators::ResourceHelpers
9
+
10
+ class_option :orm, type: :string, default: "active_record"
11
+
12
+ namespace "proxied"
13
+ source_root File.expand_path("../templates", __FILE__)
14
+
15
+ desc "Generates a model with the given NAME (if one does not exist) with devise " \
16
+ "configuration plus a migration file and devise routes."
17
+
18
+ hook_for :orm, required: true
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ ::Proxied.configure do |config|
2
+ config.proxy_class = <%= class_name.camelize.to_s %> # Must be set to the ActiveRecord or Mongoid model that will be used for managing proxies
3
+
4
+ config.minimum_successful_attempts = 1
5
+ config.maximum_failed_attempts = 10
6
+
7
+ config.faraday = {
8
+ adapter: :net_http,
9
+ user_agent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.1 Safari/605.1.15",
10
+ verbose: false
11
+ }
12
+
13
+ config.http_test = {
14
+ url: "http://www.google.com/robots.txt",
15
+ timeout: 10,
16
+ }
17
+
18
+ config.socks_test = {
19
+ hostname: "whois.verisign-grs.com",
20
+ port: 43,
21
+ query: "=google.com",
22
+ timeout: 10
23
+ }
24
+
25
+ config.logger = defined?(Rails) ? -> (message) { Rails.logger.info(message) } : -> (message) { puts(message) }
26
+ end
data/lib/proxied.rb ADDED
@@ -0,0 +1,43 @@
1
+ # Gems
2
+ require "faraday"
3
+ require "net/ssh/proxy/socks5"
4
+
5
+ # Standard library
6
+ require "socket"
7
+
8
+ # Gem
9
+ require "proxied/version"
10
+
11
+ require "proxied/configuration"
12
+ require "proxied/logger"
13
+
14
+ require "proxied/shared"
15
+ require "proxied/sql/proxy_methods" if defined?(ActiveRecord)
16
+ require "proxied/nosql/proxy_methods" if defined?(Mongoid)
17
+
18
+ require "proxied/checker"
19
+
20
+ if defined?(Sidekiq)
21
+ require "proxied/jobs/check_proxies_job"
22
+ require "proxied/jobs/check_proxy_job"
23
+ end
24
+
25
+ require "proxied/railtie" if defined?(Rails)
26
+
27
+ module Proxied
28
+ class << self
29
+ attr_writer :configuration
30
+ end
31
+
32
+ def self.configuration
33
+ @configuration ||= ::Proxied::Configuration.new
34
+ end
35
+
36
+ def self.reset
37
+ @configuration = ::Proxied::Configuration.new
38
+ end
39
+
40
+ def self.configure
41
+ yield(configuration)
42
+ end
43
+ end
Binary file
@@ -0,0 +1,123 @@
1
+ module Proxied
2
+ class Checker
3
+ attr_accessor :minimum_successful_attempts, :maximum_failed_attempts
4
+ attr_accessor :limit
5
+
6
+ def initialize(minimum_successful_attempts: ::Proxied.configuration.minimum_successful_attempts, maximum_failed_attempts: ::Proxied.configuration.maximum_failed_attempts, limit: nil)
7
+ self.minimum_successful_attempts = minimum_successful_attempts
8
+ self.maximum_failed_attempts = maximum_failed_attempts
9
+ self.limit = limit
10
+ end
11
+
12
+ def check_proxies(protocol: :all, proxy_type: :all, mode: :synchronous)
13
+ proxies = ::Proxied.configuration.proxy_class.should_be_checked(
14
+ protocol: protocol,
15
+ proxy_type: proxy_type,
16
+ date: Time.now,
17
+ limit: self.limit,
18
+ maximum_failed_attempts: self.maximum_failed_attempts
19
+ )
20
+
21
+ if proxies&.any?
22
+ ::Proxied::Logger.log "Found #{proxies.size} #{proxy_type} proxies to check."
23
+
24
+ proxies.each do |proxy|
25
+ case mode
26
+ when :synchronous
27
+ check_proxy(proxy)
28
+ when :sidekiq
29
+ ::Proxied::Jobs::CheckProxyJob.perform_async(proxy.id.to_s)
30
+ end
31
+ end
32
+
33
+ else
34
+ ::Proxied::Logger.log "Couldn't find any proxies to check!"
35
+ end
36
+ end
37
+
38
+ def check_proxy(proxy, update: true)
39
+ ::Proxied::Logger.log "#{Time.now}: Will check if proxy #{proxy.proxy_address} is working."
40
+
41
+ self.send("check_#{proxy.protocol}_proxy", proxy, update: update)
42
+ end
43
+
44
+ def check_socks_proxy(proxy, test_host: ::Proxied.configuration.socks_test[:hostname], test_port: ::Proxied.configuration.socks_test[:port], test_query: ::Proxied.configuration.socks_test[:query], timeout: ::Proxied.configuration.socks_test[:timeout], update: true)
45
+ valid_proxy = false
46
+
47
+ begin
48
+ socks_proxy = ::Net::SSH::Proxy::SOCKS5.new(proxy.host, proxy.port, proxy.socks_proxy_credentials)
49
+ client = socks_proxy.open(test_host, test_port, {timeout: timeout})
50
+
51
+ client.write("#{test_query}\r\n")
52
+ response = client.read
53
+
54
+ valid_proxy = !response.to_s.empty?
55
+
56
+ client.close
57
+
58
+ rescue StandardError => e
59
+ ::Proxied::Logger.log "Exception occured while trying to check proxy #{proxy.proxy_address}. Error Class: #{e.class}. Error Message: #{e.message}"
60
+ valid_proxy = false
61
+ end
62
+
63
+ update_proxy(proxy, valid_proxy) if update
64
+
65
+ return valid_proxy
66
+ end
67
+
68
+ def check_http_proxy(proxy, test_url: ::Proxied.configuration.http_test[:url], timeout: ::Proxied.configuration.http_test[:timeout], update: true)
69
+ ::Proxied::Logger.log "#{Time.now}: Fetching robots.txt for Google.com with proxy #{proxy.proxy_address}. Using authentication? #{proxy.proxy_credentials}"
70
+
71
+ response = request(test_url, proxy, options: {timeout: timeout})
72
+ valid_proxy = (response && !(response =~ /Allow: \/search\/about/i).nil?)
73
+
74
+ update_proxy(proxy, valid_proxy) if update
75
+
76
+ return valid_proxy
77
+ end
78
+
79
+ def update_proxy(proxy, valid)
80
+ ::Proxied::Logger.log "#{Time.now}: Proxy #{proxy.proxy_address} is #{valid ? "working" : "not working"}!"
81
+
82
+ successful_attempts = proxy.successful_attempts || 0
83
+ failed_attempts = proxy.failed_attempts || 0
84
+
85
+ if valid
86
+ successful_attempts += 1
87
+ else
88
+ failed_attempts += 1
89
+ end
90
+
91
+ is_valid = (successful_attempts >= self.minimum_successful_attempts && failed_attempts < self.maximum_failed_attempts)
92
+
93
+ proxy.valid_proxy = is_valid
94
+ proxy.successful_attempts = successful_attempts
95
+ proxy.failed_attempts = failed_attempts
96
+ proxy.last_checked_at = Time.now
97
+ proxy.save
98
+ end
99
+
100
+ private
101
+ def request(url, proxy, options: {})
102
+ response = nil
103
+
104
+ user_agent = options.fetch(:user_agent, ::Proxied.configuration.faraday.fetch(:user_agent, nil))
105
+ timeout = options.fetch(:timeout, ::Proxied.configuration.http_test[:timeout])
106
+
107
+ begin
108
+ response = ::Faraday.new(url: url) do |builder|
109
+ builder.headers["User-Agent"] = user_agent if !user_agent.to_s.empty?
110
+ builder.options[:timeout] = timeout if timeout
111
+ builder.proxy = proxy.proxy_options_for_faraday
112
+ builder.response :logger if ::Proxied.configuration.verbose_faraday?
113
+ builder.adapter ::Proxied.configuration.faraday.fetch(:adapter, :net_http)
114
+ end.get&.body
115
+ rescue Faraday::Error => e
116
+ ::Proxied::Logger.log "Exception occured while trying to check proxy #{proxy.proxy_address}. Error Class: #{e.class}. Error Message: #{e.message}"
117
+ end
118
+
119
+ return response
120
+ end
121
+
122
+ end
123
+ end