multitunnels 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5f6ffdbbf77a056e8f65856cb0cadf28981a6603
4
+ data.tar.gz: 13552a5f830d8b620d59192b98568c62a04a4057
5
+ SHA512:
6
+ metadata.gz: 59ec67f3950c913fd4ea360a1ff072bf1d883a21073b9f9420ea6388ce07c15f252772db3919ef644d17d0efa7d4252df84a311d4ab724f255d1c66de3bd34ff
7
+ data.tar.gz: 7068d4edd06ca66d1444c7e5ba9cb14473b19ee5e0bfd77521fc2967cf8e9299a9773a2eb3666303a8dba7cfc0b3f2363de59bca0f16d12453ae9903acd8fd09
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ Gemfile.lock
2
+ pkg/
3
+ *~
4
+ *.swp
5
+ *.tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in tunnels.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ Tunnels
2
+ =======
3
+
4
+ ![image](http://i.imgur.com/Ej5dz.png)
5
+
6
+ Tunnels is a proxy to http/https from http/https.
7
+
8
+ You can run the [Pow](http://pow.cx/) over SSL!
9
+
10
+ Installation
11
+ ------------
12
+
13
+ $ gem install multitunnels
14
+
15
+ Run
16
+ ---
17
+
18
+ $ sudo multitunnels
19
+
20
+ If you are using rvm:
21
+
22
+ $ rvmsudo multitunnels
23
+
24
+ By default, proxy to 80 port from 443 port.
25
+
26
+ specify "http" port and "https" port:
27
+
28
+ $ sudo multitunnels 443 3000
29
+
30
+ or
31
+
32
+ $ sudo multitunnels 127.0.0.1:443 127.0.0.1:3000
33
+ or
34
+
35
+ $ sudo multitunnels https://127.0.0.1:443 http://127.0.0.1:3000
36
+
37
+ You can also proxy to 443 port from 80 port.
38
+
39
+ $ sudo multitunnels http://127.0.0.1:80 https://127.0.0.1:443
40
+
41
+ If hostname is '127.0.0.1' or 'localhost', you can choose to omit them.
42
+
43
+ $ sudo multitunnels http://:80 https://:443
44
+
45
+ You can proxy to other host, then you can `curl http://localhost:3000` to visit Google Hongkong
46
+
47
+ $ multitunnels http://:3000 https://www.google.com.hk:443
48
+
49
+ You can proxy from Baidu to Google :) someone might be confused (do not forget to add entry `127.0.0.1 www.baidu.com` to /etc/hosts)
50
+
51
+ $ sudo multitunnels http://www.baidu.com https://www.google.com.hk
52
+
53
+ As the name implies, you can create lots of proxies in one command (but do not forget to add entry `127.0.0.1 www.baidu.com www.samsung.com` to /etc/hosts)
54
+
55
+ $ sudo multitunnels http://www.baidu.com https://www.google.com.hk http://www.samsung.com http://www.apple.com 443 80 8000 3000
56
+
57
+ This command creates four proxies.
58
+
59
+ Copyright
60
+ ---------
61
+
62
+ Copyright (c) 2012 jugyo, released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/multitunnels ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'tunnels'
3
+ unless ARGV.size.even?
4
+ STDERR.puts Tunnels.help
5
+ exit!
6
+ end
7
+
8
+ begin
9
+ Tunnels.run!(ARGV)
10
+ rescue ArgumentError
11
+ STDERR.puts Tunnels.help
12
+ exit!
13
+ end
data/lib/tunnels.rb ADDED
@@ -0,0 +1,232 @@
1
+ require 'tunnels/version'
2
+ require 'uri'
3
+ require 'eventmachine'
4
+
5
+ # most of code is from [thin-glazed](https://github.com/freelancing-god/thin-glazed).
6
+ # Copyright © 2012, Thin::Glazed was a Rails Camp New Zealand project, and is developed and maintained by Pat Allan. It is released under the open MIT Licence.
7
+
8
+ class ClientError < StandardError; end
9
+
10
+ module Tunnels
11
+ def self.run!(params)
12
+ maps = parse_params params
13
+
14
+ maps.each do |from, to|
15
+ puts "#{from[0]}://#{from[1]}:#{from[2]} --(--)--> #{to[0]}://#{to[1]}:#{to[2]}"
16
+ end
17
+
18
+ maps = group_params maps
19
+
20
+ EventMachine.run do
21
+ servers = {}
22
+ maps.each do |from, map|
23
+ scheme, port = from
24
+ servers[from] ||= case scheme
25
+ when :https
26
+ EventMachine.start_server('127.0.0.1', port, HttpsProxy, map)
27
+ when :http
28
+ EventMachine.start_server('127.0.0.1', port, HttpProxy, map)
29
+ end
30
+ end
31
+ puts "Ready :)"
32
+ end
33
+ rescue RuntimeError => e
34
+ STDERR.puts e.message
35
+ STDERR.puts "Maybe you should run on `sudo`"
36
+ rescue ClientError => e
37
+ STDERR.puts e.message
38
+ end
39
+
40
+ def self.help
41
+ <<-HELP
42
+ Usage:
43
+ tunnels [from to] [from2 to2] ...
44
+
45
+ Examples:
46
+ tunnels 443 3000
47
+ tunnels localhost:443 localhost:3000
48
+ tunnels https://:443 http://:3000
49
+ tunnels https://localhost:443 http://localhost:3000
50
+
51
+ HELP
52
+ end
53
+
54
+ private
55
+ def self.parse_params params = []
56
+ params = ['443', '80'] if params.empty?
57
+ maps = split_params params
58
+ maps.inject({}) do |h, (from, to)|
59
+ h.merge parse_host_str(from) => parse_host_str(to)
60
+ end
61
+ end
62
+
63
+ def self.group_params params = {}
64
+ params.inject({}) do |h, (from, to)|
65
+ h[[from[0], from[2]]] ||= {}
66
+ h[[from[0], from[2]]].merge! from => to
67
+ h
68
+ end
69
+ end
70
+
71
+ def self.split_params params = []
72
+ Hash[*params]
73
+ end
74
+
75
+ def self.parse_host_str str
76
+ raise ArgumentError if str.empty?
77
+ if str =~ /^:?(\d+)$/
78
+ [get_scheme($1), '127.0.0.1', $1.to_i]
79
+ elsif str =~ /^(\w+):\/\/:(\d+)$/
80
+ [$1.to_sym, '127.0.0.1', $2.to_i]
81
+ elsif str =~ /:\/\//
82
+ uri = URI str
83
+ [uri.scheme.to_sym, uri.host, uri.port]
84
+ else
85
+ parts = str.split(':')
86
+ raise ArgumentError if parts.size != 2
87
+ [get_scheme(parts[1]), parts[0], parts[1].to_i]
88
+ end
89
+ end
90
+
91
+ def self.get_scheme port
92
+ port.to_i == 443 ? :https : :http
93
+ end
94
+
95
+ class HttpClient < EventMachine::Connection
96
+ attr_reader :proxy, :to_host, :to_port
97
+
98
+ def initialize(proxy, from_host, from_port, to_host, to_port)
99
+ @proxy = proxy
100
+ @connected = EventMachine::DefaultDeferrable.new
101
+ @from_host, @from_port, @to_host, @to_port = from_host, from_port, to_host, to_port
102
+ end
103
+
104
+ def connection_completed
105
+ @connected.succeed
106
+ end
107
+
108
+ def receive_data(data)
109
+ proxy.relay_from_client(data)
110
+ end
111
+
112
+ def send(data)
113
+ @connected.callback { send_data data }
114
+ end
115
+
116
+ def unbind
117
+ proxy.unbind_client @from_host, @from_port
118
+ end
119
+ end
120
+
121
+ class HttpsClient < HttpClient
122
+ def post_init
123
+ start_tls
124
+ end
125
+ end
126
+
127
+ class HttpProxy < EventMachine::Connection
128
+ def initialize(map)
129
+ map.each do |from, to|
130
+ make_client from[1], from[2], to[0], to[1], to[2]
131
+ end
132
+ end
133
+
134
+ def receive_data(data)
135
+ unless data.nil?
136
+ client = nil
137
+ data.sub! /\r\nHost:\s*(.+?)\s*\r\n/i do
138
+ host, port = $1.split(':') if $1
139
+ client = client host, port
140
+ "\r\nHost: #{client.to_host}:#{client.to_port}\r\n"
141
+ end
142
+
143
+ client.send_data data
144
+ end
145
+ end
146
+
147
+ def relay_from_client(data)
148
+ send_data data unless data.nil?
149
+ end
150
+
151
+ def unbind
152
+ close_all_connections
153
+ clear_clients
154
+ end
155
+
156
+ def unbind_client host, port
157
+ close_connection_after_writing
158
+ clear_client host, port.to_i
159
+ end
160
+
161
+ private
162
+ def make_client from_host, from_port, to_scheme, to_host, to_port
163
+ from_port, to_port = from_port.to_i, to_port.to_i
164
+ @clients ||= Hash.new {|h, k| h[k] = {} }
165
+ @clients[from_host][from_port] = case to_scheme
166
+ when :http
167
+ EventMachine.connect to_host, to_port, HttpClient, self, from_host, from_port, to_host, to_port
168
+ when :https
169
+ EventMachine.connect to_host, to_port, HttpsClient, self, from_host, from_port, to_host, to_port
170
+ else
171
+ raise ClientError.new "Cannot support scheme: #{to_scheme}"
172
+ end
173
+ end
174
+
175
+ def client host, port
176
+ port = port.to_i
177
+ search_client_by_host_and_port(host, port) ||
178
+ search_client_by_host(host) ||
179
+ raise(ClientError.new "Cannot find a client for #{host}:#{port}")
180
+ end
181
+
182
+ def search_client_by_host_and_port host, port
183
+ @clients[host][port] ||
184
+ @clients['localhost'][port] ||
185
+ @clients['127.0.0.1'][port] ||
186
+ @clients['0.0.0.0'][port]
187
+ end
188
+
189
+ def search_client_by_host host
190
+ @clients[host].values.first ||
191
+ @clients['localhost'].values.first ||
192
+ @clients['127.0.0.1'].values.first ||
193
+ @clients['0.0.0.0'].values.first
194
+ end
195
+
196
+ def clear_client host, port
197
+ @clients[host].delete port.to_i
198
+ end
199
+
200
+ def close_all_connections
201
+ @clients.each do |_, host|
202
+ host.each do |_, client|
203
+ client.close_connection
204
+ end
205
+ end
206
+ end
207
+
208
+ def clear_clients
209
+ @clients.clear
210
+ end
211
+ end
212
+
213
+ class HttpsProxy < HttpProxy
214
+ def post_init
215
+ start_tls
216
+ end
217
+
218
+ def relay_from_client(data)
219
+ super
220
+ @x_forwarded_proto_header_inserted = false
221
+ end
222
+
223
+ def receive_data(data)
224
+ if !@x_forwarded_proto_header_inserted && data =~ /\r\n\r\n/
225
+ super data.gsub(/\r\n\r\n/, "\r\nX_FORWARDED_PROTO: https\r\n\r\n")
226
+ @x_forwarded_proto_header_inserted = true
227
+ else
228
+ super
229
+ end
230
+ end
231
+ end
232
+ end
@@ -0,0 +1,3 @@
1
+ module Tunnels
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'rspec'
2
+ require 'tunnels'
3
+
4
+ RSpec.configure do |config|
5
+ config.mock_with :rr
6
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ # to stub
4
+ def EventMachine.run(&block)
5
+ block.call
6
+ end
7
+
8
+ describe Tunnels do
9
+ describe '.run!' do
10
+ context 'with no args' do
11
+ it 'works' do
12
+ mock(EventMachine).start_server('127.0.0.1', 443, Tunnels::HttpsProxy, 80)
13
+ Tunnels.run!
14
+ end
15
+ end
16
+
17
+ context 'with args' do
18
+ it 'works' do
19
+ mock(EventMachine).start_server('127.0.0.1', 443, Tunnels::HttpsProxy, 80)
20
+ Tunnels.run!('127.0.0.1:443', '127.0.0.1:80')
21
+ end
22
+ end
23
+ end
24
+ end
data/tunnels.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'tunnels/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'multitunnels'
7
+ s.version = Tunnels::VERSION
8
+ s.authors = ['jugyo', 'bachue']
9
+ s.email = ['jugyo.org@gmail.com', 'bachue.shu@gmail.com']
10
+ s.homepage = "https://github.com/bachue/tunnels"
11
+ s.summary = %q{https/http --(--)--> https/http}
12
+ s.description = %q{This tunnels http/https to http/https.}
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ['lib']
18
+
19
+ # specify any dependencies here; for example:
20
+ # s.add_development_dependency "rspec"
21
+ s.add_development_dependency 'rspec'
22
+ s.add_development_dependency 'rr'
23
+ s.add_runtime_dependency 'eventmachine'
24
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multitunnels
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - jugyo
8
+ - bachue
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-06-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rr
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: eventmachine
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: This tunnels http/https to http/https.
57
+ email:
58
+ - jugyo.org@gmail.com
59
+ - bachue.shu@gmail.com
60
+ executables:
61
+ - multitunnels
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - ".gitignore"
66
+ - Gemfile
67
+ - README.md
68
+ - Rakefile
69
+ - bin/multitunnels
70
+ - lib/tunnels.rb
71
+ - lib/tunnels/version.rb
72
+ - spec/spec_helper.rb
73
+ - spec/tunnels_spec.rb
74
+ - tunnels.gemspec
75
+ homepage: https://github.com/bachue/tunnels
76
+ licenses: []
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.2.2
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: https/http --(--)--> https/http
98
+ test_files:
99
+ - spec/spec_helper.rb
100
+ - spec/tunnels_spec.rb
101
+ has_rdoc: