proxied 0.1.0

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