multitunnels 1.0.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 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: