├── .rspec ├── VERSION ├── .document ├── lib ├── tor_requests.rb └── tor │ ├── tor_requests.rb │ ├── configuration.rb │ └── http.rb ├── Gemfile ├── spec ├── spec_helper.rb ├── integration_spec.rb ├── configuration_spec.rb └── http_spec.rb ├── .project ├── .gitignore ├── LICENSE.txt ├── Rakefile ├── Gemfile.lock ├── README.rdoc └── tor_requests.gemspec /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.6.0 2 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | bin/* 3 | - 4 | features/**/*.feature 5 | LICENSE.txt 6 | -------------------------------------------------------------------------------- /lib/tor_requests.rb: -------------------------------------------------------------------------------- 1 | require "tor/configuration" 2 | require "tor/http" 3 | require "tor/tor_requests" 4 | -------------------------------------------------------------------------------- /lib/tor/tor_requests.rb: -------------------------------------------------------------------------------- 1 | module Tor 2 | class << self 3 | def configuration 4 | config 5 | end 6 | 7 | def configure 8 | config 9 | yield(configuration) 10 | end 11 | 12 | private 13 | 14 | def config 15 | @onfiguration ||= Configuration.new 16 | end 17 | 18 | end 19 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | # Add dependencies required to use your gem here. 3 | # Example: 4 | # gem "activesupport", ">= 2.3.5" 5 | 6 | # Add dependencies to develop your gem here. 7 | # Include everything needed to run rake, tests, features, etc. 8 | group :development do 9 | gem "rspec", "~> 2.11.0" 10 | gem "rdoc", "~> 3.12" 11 | gem "bundler", "~> 1.3.5" 12 | gem "jeweler", "~> 2.0.1" 13 | gem "simplecov" 14 | gem "socksify" 15 | end -------------------------------------------------------------------------------- /lib/tor/configuration.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'socksify/http' 3 | 4 | module Tor 5 | 6 | class Configuration 7 | 8 | attr_accessor :ip, 9 | :port 10 | 11 | attr_reader :headers 12 | 13 | def add_header(header, value) 14 | @headers[header] = value 15 | end 16 | 17 | def initialize 18 | @ip = '127.0.0.1' 19 | @port = 9050 20 | @headers = Hash.new 21 | end 22 | 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 2 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 3 | require 'rspec' 4 | require 'tor/http' 5 | require 'tor/configuration' 6 | require 'tor/tor_requests' 7 | 8 | # Requires supporting files with custom matchers and macros, etc, 9 | # in ./support/ and its subdirectories. 10 | Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f} 11 | 12 | RSpec.configure do |config| 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | tor_requests 4 | 5 | 6 | 7 | 8 | 9 | com.aptana.ide.core.unifiedBuilder 10 | 11 | 12 | 13 | 14 | 15 | com.aptana.ruby.core.rubynature 16 | com.aptana.projects.webnature 17 | 18 | 19 | -------------------------------------------------------------------------------- /spec/integration_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | require 'net/http' 3 | require 'socksify/http' 4 | 5 | describe "Configure" do 6 | 7 | context "different values" do 8 | it "calls correctly" do 9 | 10 | Tor.configure do |config| 11 | config.ip = "a" 12 | config.port = 9051 13 | config.add_header('User-Agent', 'Netscape 2.0') 14 | end 15 | 16 | proxy = Net::HTTP.SOCKSProxy("127.0.0.1", 9050) 17 | proxy.stub(:start) 18 | Net::HTTP.should_receive(:SOCKSProxy).with("a", 9051).and_return( proxy) 19 | Tor::HTTP.get(URI('http://google.com/')) 20 | end 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # rcov generated 2 | coverage 3 | coverage.data 4 | 5 | # rdoc generated 6 | rdoc 7 | 8 | # yard generated 9 | doc 10 | .yardoc 11 | 12 | # bundler 13 | .bundle 14 | 15 | # jeweler generated 16 | pkg 17 | 18 | # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore: 19 | # 20 | # * Create a file at ~/.gitignore 21 | # * Include files you want ignored 22 | # * Run: git config --global core.excludesfile ~/.gitignore 23 | # 24 | # After doing this, these files will be ignored in all your git projects, 25 | # saving you from having to 'pollute' every project you touch with them 26 | # 27 | # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line) 28 | # 29 | # For MacOS: 30 | # 31 | #.DS_Store 32 | 33 | # For TextMate 34 | #*.tmproj 35 | #tmtags 36 | 37 | # For emacs: 38 | #*~ 39 | #\#* 40 | #.\#* 41 | 42 | # For vim: 43 | #*.swp 44 | 45 | # For redcar: 46 | #.redcar 47 | 48 | # For rubinius: 49 | #*.rbc 50 | -------------------------------------------------------------------------------- /spec/configuration_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | 4 | describe "Configure" do 5 | 6 | it "default port" do 7 | Tor::Configuration.new.port.should eq(9050) 8 | end 9 | 10 | it "default headers" do 11 | Tor::Configuration.new.headers.should eq(Hash.new) 12 | end 13 | 14 | 15 | it "default ip" do 16 | Tor::Configuration.new.ip.should eq('127.0.0.1') 17 | end 18 | 19 | context "initialize" do 20 | 21 | it "default headers" do 22 | config = Tor::Configuration.new 23 | config.add_header('User-Agent', 'Netscape 2.0') 24 | config.headers.should eq({'User-Agent' => 'Netscape 2.0'}) 25 | end 26 | 27 | 28 | it "default port" do 29 | config = Tor::Configuration.new 30 | config.port = 99 31 | config.port.should eq(99) 32 | end 33 | 34 | it "default ip" do 35 | config = Tor::Configuration.new 36 | config.ip = "whatever" 37 | config.ip.should eq("whatever") 38 | end 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Bruno Ghisi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'rubygems' 4 | require 'bundler' 5 | begin 6 | Bundler.setup(:default, :development) 7 | rescue Bundler::BundlerError => e 8 | $stderr.puts e.message 9 | $stderr.puts "Run `bundle install` to install missing gems" 10 | exit e.status_code 11 | end 12 | require 'rake' 13 | 14 | require 'jeweler' 15 | Jeweler::Tasks.new do |gem| 16 | # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options 17 | gem.name = "tor_requests" 18 | gem.homepage = "http://github.com/brunogh/tor_requests" 19 | gem.license = "MIT" 20 | gem.summary = "Create anonymously requests through Tor network" 21 | gem.description = "Create anonymously requests through Tor network" 22 | gem.email = "brunogh@gmail.com" 23 | gem.authors = ["Bruno Ghisi"] 24 | # dependencies defined in Gemfile 25 | gem.add_dependency 'socksify' 26 | end 27 | Jeweler::RubygemsDotOrgTasks.new 28 | 29 | require 'rspec/core' 30 | require 'rspec/core/rake_task' 31 | RSpec::Core::RakeTask.new(:spec) do |spec| 32 | spec.pattern = FileList['spec/**/*_spec.rb'] 33 | end 34 | 35 | RSpec::Core::RakeTask.new(:rcov) do |spec| 36 | spec.pattern = 'spec/**/*_spec.rb' 37 | spec.rcov = true 38 | end 39 | 40 | task :default => :spec 41 | 42 | require 'rdoc/task' 43 | Rake::RDocTask.new do |rdoc| 44 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 45 | 46 | rdoc.rdoc_dir = 'rdoc' 47 | rdoc.title = "tor_requests #{version}" 48 | rdoc.rdoc_files.include('README*') 49 | rdoc.rdoc_files.include('lib/**/*.rb') 50 | end 51 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | addressable (2.3.6) 5 | builder (3.2.2) 6 | diff-lcs (1.1.3) 7 | faraday (0.7.6) 8 | addressable (~> 2.2) 9 | multipart-post (~> 1.1) 10 | rack (~> 1.1) 11 | git (1.2.6) 12 | github_api (0.4.10) 13 | faraday (~> 0.7.6) 14 | hashie (~> 1.2.0) 15 | multi_json (~> 1.0) 16 | oauth2 (~> 0.5.2) 17 | hashie (1.2.0) 18 | highline (1.6.21) 19 | jeweler (2.0.1) 20 | builder 21 | bundler (>= 1.0) 22 | git (>= 1.2.5) 23 | github_api 24 | highline (>= 1.6.15) 25 | nokogiri (>= 1.5.10) 26 | rake 27 | rdoc 28 | json (1.7.3) 29 | mini_portile (0.5.3) 30 | multi_json (1.3.6) 31 | multipart-post (1.2.0) 32 | nokogiri (1.6.1) 33 | mini_portile (~> 0.5.0) 34 | oauth2 (0.5.2) 35 | faraday (~> 0.7) 36 | multi_json (~> 1.0) 37 | rack (1.5.2) 38 | rake (10.2.2) 39 | rdoc (3.12) 40 | json (~> 1.4) 41 | rspec (2.11.0) 42 | rspec-core (~> 2.11.0) 43 | rspec-expectations (~> 2.11.0) 44 | rspec-mocks (~> 2.11.0) 45 | rspec-core (2.11.1) 46 | rspec-expectations (2.11.3) 47 | diff-lcs (~> 1.1.3) 48 | rspec-mocks (2.11.3) 49 | simplecov (0.6.4) 50 | multi_json (~> 1.0) 51 | simplecov-html (~> 0.5.3) 52 | simplecov-html (0.5.3) 53 | socksify (1.4.1) 54 | 55 | PLATFORMS 56 | ruby 57 | 58 | DEPENDENCIES 59 | bundler (~> 1.3.5) 60 | jeweler (~> 2.0.1) 61 | rdoc (~> 3.12) 62 | rspec (~> 2.11.0) 63 | simplecov 64 | socksify 65 | -------------------------------------------------------------------------------- /spec/http_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper') 2 | 3 | describe "Http" do 4 | 5 | describe "get" do 6 | 7 | context "with invalid parameters" do 8 | it "does not work" do 9 | expect { Tor::HTTP.get("google.com") }.to raise_error 10 | end 11 | end 12 | 13 | context "with URI parameter" do 14 | ["http", "https"].each do |protocol| 15 | it "follows the #{protocol} redirects" do 16 | res = Tor::HTTP.get(URI("#{protocol}://google.com/")) 17 | res.code.should eq("200") 18 | end 19 | 20 | context "with custom redirects limit" do 21 | it "raises TooManyRedirects error after 1 retry" do 22 | expect { Tor::HTTP.get(URI("#{protocol}://bit.ly/1ngrqeH"), nil, nil, 1) }.to raise_error("Tor::HTTP::TooManyRedirects") 23 | end 24 | end 25 | 26 | end 27 | end 28 | 29 | context "with host, path and port parameters" do 30 | it "works" do 31 | res = Tor::HTTP.get("google.com", "/", 80) 32 | res.code.should eq("200") 33 | end 34 | end 35 | 36 | end 37 | 38 | describe "post" do 39 | 40 | context "with invalid parameters" do 41 | it "does not work" do 42 | expect { Tor::HTTP.post("google.com") }.to raise_error 43 | end 44 | end 45 | 46 | context "with URI parameter" do 47 | ["http", "https"].each do |protocol| 48 | it "works with #{protocol}" do 49 | res = Tor::HTTP.post(URI("#{protocol}://posttestserver.com/post.php?dir=example"), {"q" => "query", "var" => "variable"}) 50 | res.code.should eq("200") 51 | end 52 | end 53 | end 54 | 55 | context "with host, path and port parameters" do 56 | it "works" do 57 | res = Tor::HTTP.post('posttestserver.com', {"q" => "query", "var" => "variable"}, '/post.php?dir=example', 80) 58 | res.code.should eq("200") 59 | end 60 | 61 | end 62 | 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = tor_requests 2 | 3 | 1 - Install Tor 4 | 5 | * Download at https://www.torproject.org 6 | * Start Tor 7 | * You can change port configuration by clicking on Settings -> Advanced -> Adjust the port to 9050 8 | 9 | 2 - Usage 10 | 11 | res = Tor::HTTP.get("google.com", "/", 80) 12 | p res.code 13 | p res.body 14 | 15 | res = Tor::HTTP.post('posttestserver.com', {"var" => "variable"}, '/post.php?dir=example', 80) 16 | p res.code 17 | p res.body 18 | 19 | with URIs (http & https) 20 | 21 | Tor::HTTP.get(URI('http://google.com/')) 22 | Tor::HTTP.get(URI('https://github.com/')) 23 | Tor::HTTP.post(URI('http://posttestserver.com/post.php?dir=example'), {"var" => "variable"}) 24 | 25 | with redirects 26 | res = Tor::HTTP.get("google.com", "/", 80, 10) 27 | By default 3 redirects are followed. 28 | To prevent infinite loops a TooManyRedirects error is raised. 29 | The limit can be changed by updating the last parameter. 30 | 31 | The default port configuration is 9050 and ip configuration is 127.0.0.1. If you need to change configuration, you can use: 32 | 33 | Tor.configure do |config| 34 | config.ip = "127.0.0.1" 35 | config.port = 9050 36 | end 37 | 38 | You can also set additional custom header fields, e.g. User-Agent: 39 | 40 | Tor.configure do |config| 41 | config.add_header('User-Agent', 'Netscape 2.0') 42 | end 43 | 44 | 45 | 46 | == Contributing to tor_requests 47 | 48 | * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet. 49 | * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it. 50 | * Fork the project. 51 | * Start a feature/bugfix branch. 52 | * Commit and push until you are happy with your contribution. 53 | * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally. 54 | * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it. 55 | 56 | == Copyright 57 | 58 | Copyright (c) 2012 Bruno Ghisi. See LICENSE.txt for 59 | further details. 60 | 61 | 62 | -------------------------------------------------------------------------------- /tor_requests.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "tor_requests" 8 | s.version = "0.6.0" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Bruno Ghisi"] 12 | s.date = "2014-04-07" 13 | s.description = "Create anonymously requests through Tor network" 14 | s.email = "brunogh@gmail.com" 15 | s.extra_rdoc_files = [ 16 | "LICENSE.txt", 17 | "README.rdoc" 18 | ] 19 | s.files = [ 20 | ".document", 21 | ".project", 22 | ".rspec", 23 | "Gemfile", 24 | "Gemfile.lock", 25 | "LICENSE.txt", 26 | "README.rdoc", 27 | "Rakefile", 28 | "VERSION", 29 | "lib/tor/configuration.rb", 30 | "lib/tor/http.rb", 31 | "lib/tor/tor_requests.rb", 32 | "lib/tor_requests.rb", 33 | "spec/configuration_spec.rb", 34 | "spec/http_spec.rb", 35 | "spec/integration_spec.rb", 36 | "spec/spec_helper.rb", 37 | "tor_requests.gemspec" 38 | ] 39 | s.homepage = "http://github.com/brunogh/tor_requests" 40 | s.licenses = ["MIT"] 41 | s.require_paths = ["lib"] 42 | s.rubygems_version = "2.0.3" 43 | s.summary = "Create anonymously requests through Tor network" 44 | 45 | if s.respond_to? :specification_version then 46 | s.specification_version = 4 47 | 48 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 49 | s.add_development_dependency(%q, ["~> 2.11.0"]) 50 | s.add_development_dependency(%q, ["~> 3.12"]) 51 | s.add_development_dependency(%q, ["~> 1.3.5"]) 52 | s.add_development_dependency(%q, ["~> 2.0.1"]) 53 | s.add_development_dependency(%q, [">= 0"]) 54 | s.add_development_dependency(%q, [">= 0"]) 55 | s.add_runtime_dependency(%q, [">= 0"]) 56 | else 57 | s.add_dependency(%q, ["~> 2.11.0"]) 58 | s.add_dependency(%q, ["~> 3.12"]) 59 | s.add_dependency(%q, ["~> 1.3.5"]) 60 | s.add_dependency(%q, ["~> 2.0.1"]) 61 | s.add_dependency(%q, [">= 0"]) 62 | s.add_dependency(%q, [">= 0"]) 63 | s.add_dependency(%q, [">= 0"]) 64 | end 65 | else 66 | s.add_dependency(%q, ["~> 2.11.0"]) 67 | s.add_dependency(%q, ["~> 3.12"]) 68 | s.add_dependency(%q, ["~> 1.3.5"]) 69 | s.add_dependency(%q, ["~> 2.0.1"]) 70 | s.add_dependency(%q, [">= 0"]) 71 | s.add_dependency(%q, [">= 0"]) 72 | s.add_dependency(%q, [">= 0"]) 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /lib/tor/http.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'socksify/http' 3 | 4 | module Tor 5 | 6 | class HTTP 7 | class TooManyRedirects < StandardError; end 8 | 9 | class << self 10 | attr_accessor :redirects_made 11 | end 12 | 13 | def self.get(uri_or_host, path = nil, port = nil, max_redirects = 3) 14 | res, host = "", nil 15 | self.redirects_made = 0 16 | 17 | if path 18 | host = uri_or_host 19 | else 20 | host = uri_or_host.host 21 | port = uri_or_host.port 22 | end 23 | 24 | start_params = start_parameters(uri_or_host, host, port) 25 | start_socks_proxy(start_params) do |http| 26 | request = Net::HTTP::Get.new(path || uri_or_host.path) 27 | Tor.configuration.headers.each do |header, value| 28 | request.delete(header) 29 | request.add_field(header, value) 30 | end 31 | 32 | res = http.request(request) 33 | res = follow_redirect(res, http, max_redirects) # Follow redirects 34 | end 35 | 36 | res 37 | end 38 | 39 | def self.post(uri_or_host, post_options = {}, path = nil, port = nil) 40 | res, host = "", nil 41 | if path 42 | host = uri_or_host 43 | else 44 | host = uri_or_host.host 45 | port = uri_or_host.port 46 | path = uri_or_host.request_uri 47 | end 48 | 49 | start_params = start_parameters(uri_or_host, host, port) 50 | start_socks_proxy(start_params) do |http| 51 | request = Net::HTTP::Post.new(path) 52 | request.set_form_data(post_options) 53 | Tor.configuration.headers.each do |header, value| 54 | request.delete(header) 55 | request.add_field(header, value) 56 | end 57 | res = http.request(request) 58 | end 59 | 60 | res 61 | end 62 | 63 | private 64 | 65 | def self.start_socks_proxy(start_params, &code_block) 66 | Net::HTTP.SOCKSProxy(Tor.configuration.ip, Tor.configuration.port). 67 | start(*start_params) { |http| code_block.call(http) } 68 | end 69 | 70 | def self.start_parameters(uri_or_host, host, port) 71 | uri_or_host = URI.parse(uri_or_host) if uri_or_host.is_a? String 72 | [ 73 | host, port, 74 | :use_ssl => uri_or_host.scheme == 'https', 75 | :verify_mode => OpenSSL::SSL::VERIFY_NONE 76 | ] 77 | end 78 | 79 | def self.follow_redirect(response, http, max_redirects) 80 | if response.kind_of?(Net::HTTPRedirection) 81 | raise TooManyRedirects if self.redirects_made >= max_redirects 82 | request = Net::HTTP::Get.new(fetch_redirect_url(response)) 83 | response = http.request(request) 84 | self.redirects_made += 1 85 | response = follow_redirect(response, http, max_redirects) 86 | else 87 | response 88 | end 89 | 90 | end 91 | 92 | # Get the redirect url from the response. 93 | # It searches in the "location" header and response body 94 | def self.fetch_redirect_url(response) 95 | if response['location'].nil? 96 | response.body.match(/]+)\">/i)[1] 97 | else 98 | response['location'] 99 | end 100 | end 101 | 102 | end 103 | 104 | end 105 | 106 | --------------------------------------------------------------------------------