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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +3 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Appraisals +13 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +202 -0
- data/LICENSE.txt +21 -0
- data/README.md +56 -0
- data/Rakefile +19 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/activerecord5.gemfile +11 -0
- data/gemfiles/mongoid7.gemfile +9 -0
- data/lib/.DS_Store +0 -0
- data/lib/generators/active_record/proxied_generator.rb +68 -0
- data/lib/generators/active_record/templates/migration.rb +24 -0
- data/lib/generators/mongoid/proxied_generator.rb +54 -0
- data/lib/generators/proxied/install_generator.rb +20 -0
- data/lib/generators/proxied/orm_helpers.rb +34 -0
- data/lib/generators/proxied/proxied_generator.rb +22 -0
- data/lib/generators/templates/proxied.rb +26 -0
- data/lib/proxied.rb +43 -0
- data/lib/proxied/.DS_Store +0 -0
- data/lib/proxied/checker.rb +123 -0
- data/lib/proxied/configuration.rb +41 -0
- data/lib/proxied/jobs/check_proxies_job.rb +12 -0
- data/lib/proxied/jobs/check_proxy_job.rb +18 -0
- data/lib/proxied/logger.rb +9 -0
- data/lib/proxied/nosql/proxy_methods.rb +60 -0
- data/lib/proxied/railtie.rb +11 -0
- data/lib/proxied/shared.rb +54 -0
- data/lib/proxied/sql/proxy_methods.rb +61 -0
- data/lib/proxied/version.rb +3 -0
- data/lib/tasks/tasks.rake +13 -0
- data/proxied.gemspec +47 -0
- metadata +332 -0
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,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: "../"
|
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
|