local_tunnel 0.1.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 +7 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +40 -0
- data/LICENSE +29 -0
- data/Rakefile +10 -0
- data/bin/local_tunnel +6 -0
- data/lib/local_tunnel/cli.rb +73 -0
- data/lib/local_tunnel.rb +232 -0
- data/test/serve/b35c88e0-d652-4d91-b425-393427cdedba +1 -0
- data/test/serve/b35c88e0-d652-4d91-b425-393427cdedba.log +10 -0
- data/test/test_local_tunnel.rb +43 -0
- metadata +112 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: f5300a92b5881f63621885ab10ce834c85d2e2f1
|
|
4
|
+
data.tar.gz: 894a2285865ce5a1e99f94c9263252bcb0d9b9bf
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0cd55b30eea1aacb60990c1a632e7b38353c5dbad6b22b5bdeaec99ab038b6bdb7e874da1defd4f6aebe1e28faf82a7872fdf482d8cf49bfd85634427e844e59
|
|
7
|
+
data.tar.gz: dace353ede608e9113e0ee67ca59d2f950b2fed21f5f05864ed1e1ca4c4588af7c56dd8a1e2e8cd2868d5fee262c91ecdee110ff767d7015b5a1265f21b6b9cb
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
local_tunnel (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
coderay (1.1.1)
|
|
10
|
+
method_source (0.8.2)
|
|
11
|
+
minitest (5.10.3)
|
|
12
|
+
mustermann (1.0.1)
|
|
13
|
+
pry (0.10.4)
|
|
14
|
+
coderay (~> 1.1.0)
|
|
15
|
+
method_source (~> 0.8.1)
|
|
16
|
+
slop (~> 3.4)
|
|
17
|
+
rack (2.0.3)
|
|
18
|
+
rack-protection (2.0.0)
|
|
19
|
+
rack
|
|
20
|
+
rake (12.0.0)
|
|
21
|
+
sinatra (2.0.0)
|
|
22
|
+
mustermann (~> 1.0)
|
|
23
|
+
rack (~> 2.0)
|
|
24
|
+
rack-protection (= 2.0.0)
|
|
25
|
+
tilt (~> 2.0)
|
|
26
|
+
slop (3.6.0)
|
|
27
|
+
tilt (2.0.8)
|
|
28
|
+
|
|
29
|
+
PLATFORMS
|
|
30
|
+
ruby
|
|
31
|
+
|
|
32
|
+
DEPENDENCIES
|
|
33
|
+
local_tunnel!
|
|
34
|
+
minitest (~> 5.10)
|
|
35
|
+
pry
|
|
36
|
+
rake (~> 12.0)
|
|
37
|
+
sinatra (~> 2.0)
|
|
38
|
+
|
|
39
|
+
BUNDLED WITH
|
|
40
|
+
1.15.4
|
data/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017, Loic Nageleisen
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
* Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Rakefile
ADDED
data/bin/local_tunnel
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'local_tunnel'
|
|
4
|
+
|
|
5
|
+
class LocalTunnel::CLI
|
|
6
|
+
def initialize(port: 8000, verbose: false)
|
|
7
|
+
@options = {}
|
|
8
|
+
@options[:port] = port
|
|
9
|
+
@options[:verbose] = verbose
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def option
|
|
13
|
+
@options
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def start
|
|
17
|
+
LocalTunnel::Tunnel.new(debug: @options[:verbose]).start(option[:port]).wait
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
def start(argv)
|
|
22
|
+
options = {}
|
|
23
|
+
|
|
24
|
+
while argv.size > 0 && argv[0].start_with?('-') && argv[0] != '--'
|
|
25
|
+
case argv[0]
|
|
26
|
+
when '-v'
|
|
27
|
+
options[:verbose] = true
|
|
28
|
+
argv.shift
|
|
29
|
+
else
|
|
30
|
+
$stderr.write("#{program_name}: illegal option -- #{argv[0]}\n")
|
|
31
|
+
$stderr.write("#{usage}\n")
|
|
32
|
+
exit SysExits::USAGE
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
options[:port] = Integer(argv[0]) if argv[0]
|
|
38
|
+
rescue TypeError, ArgumentError
|
|
39
|
+
$stderr.write("#{usage}\n")
|
|
40
|
+
exit SysExits::USAGE
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
new(options).start
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def program_name
|
|
47
|
+
File.basename($PROGRAM_NAME)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def usage
|
|
51
|
+
"usage: #{program_name} [-v] [port]"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module SysExits
|
|
56
|
+
OK = 0
|
|
57
|
+
USAGE = 64
|
|
58
|
+
DATAERR = 65
|
|
59
|
+
NOINPUT = 66
|
|
60
|
+
NOUSER = 67
|
|
61
|
+
NOHOST = 68
|
|
62
|
+
UNAVAILABLE = 69
|
|
63
|
+
SOFTWARE = 70
|
|
64
|
+
OSERR = 71
|
|
65
|
+
OSFILE = 72
|
|
66
|
+
CANTCREAT = 73
|
|
67
|
+
IOERR = 74
|
|
68
|
+
TEMPFAIL = 75
|
|
69
|
+
PROTOCOL = 76
|
|
70
|
+
NOPERM = 77
|
|
71
|
+
CONFIG = 78
|
|
72
|
+
end
|
|
73
|
+
end
|
data/lib/local_tunnel.rb
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LocalTunnel; end
|
|
4
|
+
|
|
5
|
+
require 'net/http'
|
|
6
|
+
require 'uri'
|
|
7
|
+
require 'json'
|
|
8
|
+
require 'logger'
|
|
9
|
+
|
|
10
|
+
module LocalTunnel
|
|
11
|
+
AssignedUrlInfo = Struct.new(
|
|
12
|
+
:id,
|
|
13
|
+
:url,
|
|
14
|
+
:port,
|
|
15
|
+
:max_conn_count,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
SERVER = 'http://localtunnel.me/'
|
|
19
|
+
|
|
20
|
+
class TunnelConn
|
|
21
|
+
def initialize(rhost, rport, lport, id:, logger: nil)
|
|
22
|
+
@rhost = rhost
|
|
23
|
+
@rport = rport
|
|
24
|
+
@lhost = 'localhost'
|
|
25
|
+
@lport = lport
|
|
26
|
+
@lrcount = 0
|
|
27
|
+
@rlcount = 0
|
|
28
|
+
@id = id
|
|
29
|
+
@logger = logger || self.class.logger.dup
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def start
|
|
33
|
+
@rconn = rconnect
|
|
34
|
+
@lconn = lconnect
|
|
35
|
+
|
|
36
|
+
@lrthr = Thread.new do
|
|
37
|
+
buf = String.new(capacity: 1024)
|
|
38
|
+
loop do
|
|
39
|
+
begin
|
|
40
|
+
logger.debug(format("%03d lr: attempting read", @id))
|
|
41
|
+
@lconn.readpartial(1024, buf)
|
|
42
|
+
rescue EOFError
|
|
43
|
+
logger.debug(format("%03d lr: read failed, reconnecting", @id))
|
|
44
|
+
@lconn.close
|
|
45
|
+
@lconn = lconnect
|
|
46
|
+
retry
|
|
47
|
+
end
|
|
48
|
+
logger.debug(format("%03d lr: read #{buf.size}", @id))
|
|
49
|
+
|
|
50
|
+
begin
|
|
51
|
+
logger.debug(format("%03d lr: attempting write", @id))
|
|
52
|
+
s = @rconn.write(buf)
|
|
53
|
+
rescue IOError
|
|
54
|
+
logger.debug(format("%03d lr: write failed, reconnecting", @id))
|
|
55
|
+
@rconn.close
|
|
56
|
+
@rconn = rconnect
|
|
57
|
+
retry
|
|
58
|
+
end
|
|
59
|
+
logger.debug(format("%03d lr: write #{s}", @id))
|
|
60
|
+
|
|
61
|
+
@lrcount += buf.size
|
|
62
|
+
logger.debug(format("%03d lr: total #{@lrcount}", @id))
|
|
63
|
+
buf.clear
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
@rlthr = Thread.new do
|
|
68
|
+
buf = String.new(capacity: 1024)
|
|
69
|
+
loop do
|
|
70
|
+
begin
|
|
71
|
+
logger.debug(format("%03d rl: attempting read", @id))
|
|
72
|
+
@rconn.readpartial(1024, buf)
|
|
73
|
+
rescue EOFError
|
|
74
|
+
logger.debug(format("%03d rl: read failed, reconnecting", @id))
|
|
75
|
+
@rconn.close
|
|
76
|
+
@rconn = rconnect
|
|
77
|
+
retry
|
|
78
|
+
end
|
|
79
|
+
logger.debug(format("%03d rl: read #{buf.size}", @id))
|
|
80
|
+
|
|
81
|
+
begin
|
|
82
|
+
logger.debug(format("%03d rl: attempting write", @id))
|
|
83
|
+
s = @lconn.write(buf)
|
|
84
|
+
rescue IOError
|
|
85
|
+
logger.debug(format("%03d rl: write failed, reconnecting", @id))
|
|
86
|
+
@lconn.close
|
|
87
|
+
@lconn = lconnect
|
|
88
|
+
retry
|
|
89
|
+
end
|
|
90
|
+
logger.debug(format("%03d rl: write #{s}", @id))
|
|
91
|
+
|
|
92
|
+
@rlcount += buf.size
|
|
93
|
+
logger.debug(format("%03d rl: total #{@rlcount}", @id))
|
|
94
|
+
buf.clear
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
self
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def wait
|
|
102
|
+
[@lrthr, @rlthr].each(&:join)
|
|
103
|
+
self
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def stop
|
|
107
|
+
[@lrthr, @rlthr].compact.each(&:kill)
|
|
108
|
+
self
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def rconnect
|
|
112
|
+
TCPSocket.new(@rhost, @rport)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def lconnect
|
|
116
|
+
TCPSocket.new(@lhost, @lport)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
def logger
|
|
122
|
+
@logger
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class << self
|
|
126
|
+
def logger
|
|
127
|
+
@logger ||= LocalTunnel.logger.dup
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def logger=(value)
|
|
131
|
+
@logger = value
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
class Tunnel
|
|
137
|
+
def initialize(domain: nil, debug: false)
|
|
138
|
+
@domain = domain
|
|
139
|
+
@logger = self.class.logger.dup
|
|
140
|
+
@logger.level = Logger::DEBUG if debug
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def url
|
|
144
|
+
assign_url! unless defined?(@url)
|
|
145
|
+
@url
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def port
|
|
149
|
+
assign_url! unless defined?(@port)
|
|
150
|
+
@port
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def max_conn_count
|
|
154
|
+
assign_url! unless defined?(@max_conn_count)
|
|
155
|
+
@max_conn_count
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def create(port)
|
|
159
|
+
assign_url!
|
|
160
|
+
logger.debug("#{url}, #{max_conn_count}")
|
|
161
|
+
@max_conn_count.times do |i|
|
|
162
|
+
@conns[i] = TunnelConn.new(URI(SERVER).host, @port, port, id: i)
|
|
163
|
+
@conns[i].start
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def start(port)
|
|
168
|
+
create(port)
|
|
169
|
+
self
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def wait
|
|
173
|
+
@conns.compact.each(&:wait)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def stop
|
|
177
|
+
@conns.compact.each(&:stop)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
def logger
|
|
183
|
+
@logger
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def assign_url!
|
|
187
|
+
info = LocalTunnel.get_assigned_url(@domain)
|
|
188
|
+
@url = info.url
|
|
189
|
+
@max_conn_count = info.max_conn_count
|
|
190
|
+
@port = info.port
|
|
191
|
+
@conns = Array.new(@max_conn_count)
|
|
192
|
+
info
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
class << self
|
|
196
|
+
def logger
|
|
197
|
+
@logger ||= LocalTunnel.logger.dup
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def logger=(value)
|
|
201
|
+
@logger = value
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
class << self
|
|
207
|
+
def logger
|
|
208
|
+
@logger ||= Logger.new(STDOUT).tap { |l| l.level = Logger:: WARN }
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def logger=(value)
|
|
212
|
+
@logger = value
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def get_assigned_url(domain = nil)
|
|
216
|
+
domain = '?new' unless domain
|
|
217
|
+
|
|
218
|
+
Net::HTTP.start(URI(SERVER).hostname) do |http|
|
|
219
|
+
req = Net::HTTP::Get.new(URI(SERVER) + domain)
|
|
220
|
+
res = http.request(req)
|
|
221
|
+
|
|
222
|
+
case res
|
|
223
|
+
when Net::HTTPSuccess
|
|
224
|
+
j = JSON.parse(res.body)
|
|
225
|
+
AssignedUrlInfo.new(j['id'], j['url'], j['port'], j['max_conn_count'])
|
|
226
|
+
else
|
|
227
|
+
raise
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
b35c88e0-d652-4d91-b425-393427cdedba
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[2017-09-12 12:36:57] INFO WEBrick 1.3.1
|
|
2
|
+
[2017-09-12 12:36:57] INFO ruby 2.4.1 (2017-03-22) [x86_64-darwin16]
|
|
3
|
+
== Sinatra (v2.0.0) has taken the stage on 8000 for development with backup from WEBrick
|
|
4
|
+
[2017-09-12 12:36:57] INFO WEBrick::HTTPServer#start: pid=35843 port=8000
|
|
5
|
+
46.218.157.254 - - [12/Sep/2017:12:36:59 +0200] "GET /b35c88e0-d652-4d91-b425-393427cdedba HTTP/1.1" 200 36 0.0127
|
|
6
|
+
== Sinatra has ended his set (crowd applauds)
|
|
7
|
+
[2017-09-12 12:37:00] INFO going to shutdown ...
|
|
8
|
+
::1 - - [12/Sep/2017:12:36:59 CEST] "GET /b35c88e0-d652-4d91-b425-393427cdedba HTTP/1.1" 200 36
|
|
9
|
+
- -> /b35c88e0-d652-4d91-b425-393427cdedba
|
|
10
|
+
[2017-09-12 12:37:00] INFO WEBrick::HTTPServer#start done.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'minitest/autorun'
|
|
4
|
+
require 'local_tunnel'
|
|
5
|
+
|
|
6
|
+
require 'securerandom'
|
|
7
|
+
require 'uri'
|
|
8
|
+
require 'net/http'
|
|
9
|
+
require 'pry'
|
|
10
|
+
require 'pathname'
|
|
11
|
+
|
|
12
|
+
class TestLocalTunnel < MiniTest::Test
|
|
13
|
+
def test_live_tunnel
|
|
14
|
+
uuid = SecureRandom.uuid.encode('UTF-8')
|
|
15
|
+
serve_dir = Pathname.new('test/serve')
|
|
16
|
+
(serve_dir + uuid).open('wb') { |f| f.write(uuid) }
|
|
17
|
+
LocalTunnel.logger = Logger.new(STDOUT).tap { |l| l.level = Logger::DEBUG }
|
|
18
|
+
|
|
19
|
+
begin
|
|
20
|
+
start_test_server(serve_dir, 8000, serve_dir + "#{uuid}.log")
|
|
21
|
+
sleep 1
|
|
22
|
+
|
|
23
|
+
t = LocalTunnel::Tunnel.new
|
|
24
|
+
t.start(8000)
|
|
25
|
+
|
|
26
|
+
res = Net::HTTP.get(URI(t.url) + uuid)
|
|
27
|
+
assert_equal(uuid, res)
|
|
28
|
+
ensure
|
|
29
|
+
t.stop if t
|
|
30
|
+
stop_test_server
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def start_test_server(path, port, out)
|
|
35
|
+
@pid = Process.spawn(<<-EOS, [:out, :err] => out.to_s)
|
|
36
|
+
ruby -rsinatra -e'set :public_folder, "#{path}"; set :port, #{port}'
|
|
37
|
+
EOS
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def stop_test_server
|
|
41
|
+
Process.kill('TERM', @pid) if @pid
|
|
42
|
+
end
|
|
43
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: local_tunnel
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Loic Nageleisen
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2017-09-12 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: minitest
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '5.10'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '5.10'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: sinatra
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: pry
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '12.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '12.0'
|
|
69
|
+
description: |2
|
|
70
|
+
Localtunnel allows you to easily share a web service on your local
|
|
71
|
+
development machine without messing with DNS and firewall settings.
|
|
72
|
+
email: loic.nageleisen@gmail.com
|
|
73
|
+
executables:
|
|
74
|
+
- local_tunnel
|
|
75
|
+
extensions: []
|
|
76
|
+
extra_rdoc_files: []
|
|
77
|
+
files:
|
|
78
|
+
- Gemfile
|
|
79
|
+
- Gemfile.lock
|
|
80
|
+
- LICENSE
|
|
81
|
+
- Rakefile
|
|
82
|
+
- bin/local_tunnel
|
|
83
|
+
- lib/local_tunnel.rb
|
|
84
|
+
- lib/local_tunnel/cli.rb
|
|
85
|
+
- test/serve/b35c88e0-d652-4d91-b425-393427cdedba
|
|
86
|
+
- test/serve/b35c88e0-d652-4d91-b425-393427cdedba.log
|
|
87
|
+
- test/test_local_tunnel.rb
|
|
88
|
+
homepage:
|
|
89
|
+
licenses:
|
|
90
|
+
- MIT
|
|
91
|
+
metadata: {}
|
|
92
|
+
post_install_message:
|
|
93
|
+
rdoc_options: []
|
|
94
|
+
require_paths:
|
|
95
|
+
- lib
|
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - ">="
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '2.3'
|
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: '0'
|
|
106
|
+
requirements: []
|
|
107
|
+
rubyforge_project:
|
|
108
|
+
rubygems_version: 2.6.13
|
|
109
|
+
signing_key:
|
|
110
|
+
specification_version: 4
|
|
111
|
+
summary: Expose yourself to the world from Ruby
|
|
112
|
+
test_files: []
|