proxied 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|