weblink 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 +7 -0
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG +3 -0
- data/VERSION +1 -0
- data/bin/weblink +76 -0
- data/lib/relay.rb +59 -0
- data/lib/weblink.rb +86 -0
- data/public/android-chrome-192x192.png +0 -0
- data/public/android-chrome-512x512.png +0 -0
- data/public/apple-touch-icon.png +0 -0
- data/public/favicon-16x16.png +0 -0
- data/public/favicon-32x32.png +0 -0
- data/public/favicon.ico +0 -0
- data/public/index.html +67 -0
- data/public/weblink.webmanifest +19 -0
- metadata +89 -0
- metadata.gz.sig +3 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c268c95b2cdcec3669dbf5c90f0753bc28949b6098744bd7c9f9e42aac24bb97
|
4
|
+
data.tar.gz: 44f0363bf19eae1f40a5a00fd962f60134a1db2a12e36aeda7bba04fe5090855
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 72ba709dd3bbdc06582bb20a83ab50090d8a60859698f4a00576122b0cd9687ed348b24d4ddbadc99219011750b897e4596605369c46e75655a821c7a76c4716
|
7
|
+
data.tar.gz: d729d225e330f6ab9714bd078893eeeecccb5d56e85f0f5d39c92f4b192a4fd9f94243789b94e0008b6264906d6a33c50f59682cb5afb215b0f21aa649202515
|
checksums.yaml.gz.sig
ADDED
data.tar.gz.sig
ADDED
Binary file
|
data/CHANGELOG
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/bin/weblink
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env -S ruby -w
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'uri'
|
8
|
+
require 'weblink'
|
9
|
+
|
10
|
+
opts = {}
|
11
|
+
|
12
|
+
op = OptionParser.new do |op|
|
13
|
+
op.summary_width = 24
|
14
|
+
|
15
|
+
op.banner =
|
16
|
+
"Usage: weblink [options]\n" \
|
17
|
+
'Web browser gateway'
|
18
|
+
|
19
|
+
op.separator ''
|
20
|
+
op.separator 'Options'
|
21
|
+
|
22
|
+
op.on('-c', '--client', 'Starts weblink client (default)') do
|
23
|
+
opts[:client] = true
|
24
|
+
end
|
25
|
+
|
26
|
+
op.on('-s', '--server', 'Starts weblink server') do
|
27
|
+
opts[:server] = true
|
28
|
+
end
|
29
|
+
|
30
|
+
op.on('-a', '--host HOST', String, 'Address to bind to (default: 0.0.0.0)') do |host|
|
31
|
+
opts[:host] = host
|
32
|
+
end
|
33
|
+
|
34
|
+
op.on('-p', '--port PORT', Integer, 'Use PORT (default: 8000)') do |port|
|
35
|
+
opts[:port] = port
|
36
|
+
end
|
37
|
+
|
38
|
+
op.on('--proxy-host HOST', String, 'Address to bind proxy to(default: 0.0.0.0)') do |host|
|
39
|
+
opts[:proxy_host] = host
|
40
|
+
end
|
41
|
+
|
42
|
+
op.on('--proxy-port PORT', Integer, 'Use proxy PORT (default: 1080)') do |port|
|
43
|
+
opts[:proxy_port] = port
|
44
|
+
end
|
45
|
+
|
46
|
+
op.on('-v', '--version', 'Show version and exit') do
|
47
|
+
version = File.expand_path('../VERSION', __dir__)
|
48
|
+
puts(File.read(version))
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
op.on_tail('-h', '--help', 'Print this help') do
|
53
|
+
puts(op)
|
54
|
+
exit
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
begin
|
59
|
+
op.parse!
|
60
|
+
rescue OptionParser::ParseError => e
|
61
|
+
op.abort(e)
|
62
|
+
else
|
63
|
+
opts[:client] = true unless opts[:client] || opts[:server]
|
64
|
+
if opts[:client] && (opts[:host] || opts[:port])
|
65
|
+
abort('Changing host or port is not supported yet in client mode')
|
66
|
+
end
|
67
|
+
opts[:host] ||= '0.0.0.0'
|
68
|
+
opts[:port] ||= 8000
|
69
|
+
opts[:proxy_host] ||= '0.0.0.0'
|
70
|
+
opts[:proxy_port] ||= 1080
|
71
|
+
end
|
72
|
+
|
73
|
+
weblink = Weblink.new(opts)
|
74
|
+
weblink.start
|
75
|
+
|
76
|
+
# vim: ft=ruby
|
data/lib/relay.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
class Relay < EventMachine::Connection
|
5
|
+
def initialize(tag, xff = nil)
|
6
|
+
pause
|
7
|
+
@tag = tag
|
8
|
+
@xff = xff
|
9
|
+
@remote_port, @remote_ip = unpack_sockaddr_in(get_peername)
|
10
|
+
@websocket = nil
|
11
|
+
@ws_remote_ip = nil
|
12
|
+
@ws_remote_port = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def start(websocket)
|
16
|
+
@websocket = websocket
|
17
|
+
@websocket.onbinary do |msg|
|
18
|
+
log('recv', msg.bytesize)
|
19
|
+
send_data(msg)
|
20
|
+
end
|
21
|
+
@websocket.onerror { |err| log('ws/error', err) }
|
22
|
+
@websocket.onclose { close_connection_after_writing }
|
23
|
+
@ws_remote_port, @ws_remote_ip = unpack_sockaddr_in(websocket.get_peername)
|
24
|
+
resume
|
25
|
+
end
|
26
|
+
|
27
|
+
def receive_data(data)
|
28
|
+
log('sent', data.bytesize)
|
29
|
+
@websocket.send_binary(data)
|
30
|
+
end
|
31
|
+
|
32
|
+
def unbind
|
33
|
+
log('close')
|
34
|
+
@websocket&.close(1000) if @websocket&.state == :connected
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def log(event, comment = nil)
|
40
|
+
row = [
|
41
|
+
Time.now.iso8601(3),
|
42
|
+
@tag,
|
43
|
+
@xff,
|
44
|
+
@remote_ip,
|
45
|
+
@remote_port&.to_s,
|
46
|
+
@ws_remote_ip,
|
47
|
+
@ws_remote_port&.to_s,
|
48
|
+
event,
|
49
|
+
comment&.to_s,
|
50
|
+
]
|
51
|
+
|
52
|
+
puts CSV.generate_line(row, col_sep: ' ', write_nil_value: '-')
|
53
|
+
end
|
54
|
+
|
55
|
+
def unpack_sockaddr_in(sock)
|
56
|
+
Socket.unpack_sockaddr_in(sock) if sock
|
57
|
+
rescue ArgumentError
|
58
|
+
end
|
59
|
+
end
|
data/lib/weblink.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'em-websocket'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'socket'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'relay'
|
6
|
+
|
7
|
+
class Weblink
|
8
|
+
def initialize(opts)
|
9
|
+
@opts = opts
|
10
|
+
@proxxxy_socket = Tempfile.new('weblink')
|
11
|
+
@websockets = EventMachine::Queue.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def start
|
15
|
+
trap(:EXIT) { Process.waitall }
|
16
|
+
|
17
|
+
if @opts[:client]
|
18
|
+
public = File.expand_path('../public', __dir__)
|
19
|
+
spawn('ruby', '-run', '-ehttpd', '--', public, err: '/dev/null')
|
20
|
+
|
21
|
+
ip = Socket.getifaddrs.find { |ifa| ifa.addr.ipv4_private? }
|
22
|
+
puts "Open http://#{ip.addr.ip_address}:8080/ on your other device." if ip
|
23
|
+
end
|
24
|
+
|
25
|
+
if @opts[:server]
|
26
|
+
begin
|
27
|
+
spawn('proxxxy', "socks5://#{@proxxxy_socket.path}")
|
28
|
+
rescue Errno::ENOENT
|
29
|
+
abort('Please install proxxxy v2 to run weblink server')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
EventMachine::WebSocket.start(@opts) do |ws|
|
34
|
+
ws.onopen do |handshake|
|
35
|
+
case handshake.path
|
36
|
+
when '/control'
|
37
|
+
start_client(ws)
|
38
|
+
puts 'Ready'
|
39
|
+
when '/client'
|
40
|
+
@websockets.push(ws)
|
41
|
+
when '/server'
|
42
|
+
proxy(ws, handshake)
|
43
|
+
else
|
44
|
+
warn("Unexpected request: #{handshake.path.inspect}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def start_client(control_ws, min_ws_num: 3)
|
53
|
+
host, port = @opts.values_at(:proxy_host, :proxy_port)
|
54
|
+
sig = EventMachine.start_server(host, port, Relay, 'client') do |rel|
|
55
|
+
# Dogpile effect
|
56
|
+
control_ws.send_text('more_ws') if @websockets.size < min_ws_num
|
57
|
+
@websockets.pop { |ws| rel.start(ws) }
|
58
|
+
end
|
59
|
+
control_ws.onclose { EventMachine.stop_server(sig) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def proxy(ws, handshake)
|
63
|
+
unless @opts[:server]
|
64
|
+
warn 'weblink server is disabled'
|
65
|
+
return
|
66
|
+
end
|
67
|
+
xff = handshake.headers_downcased['x-forwarded-for']
|
68
|
+
with_retry(3) do
|
69
|
+
EventMachine.connect(@proxxxy_socket.path, Relay, 'server', xff) do |rel|
|
70
|
+
rel.start(ws)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def with_retry(timeout, wait: 0.1)
|
76
|
+
elapsed = 0
|
77
|
+
begin
|
78
|
+
yield
|
79
|
+
rescue RuntimeError
|
80
|
+
if elapsed < timeout
|
81
|
+
elapsed += sleep(wait)
|
82
|
+
retry
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/public/favicon.ico
ADDED
Binary file
|
data/public/index.html
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<meta charset="utf-8" />
|
4
|
+
<title>weblink</title>
|
5
|
+
<link rel="manifest" href="/weblink.webmanifest">
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
background-color: #3f9cff;
|
9
|
+
color: white;
|
10
|
+
}
|
11
|
+
h1 {
|
12
|
+
align-items: center;
|
13
|
+
display: flex;
|
14
|
+
font-family: sans-serif;
|
15
|
+
font-size: 8em;
|
16
|
+
justify-content: center;
|
17
|
+
height: 100%;
|
18
|
+
}
|
19
|
+
</style>
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
<h1>weblink</h1>
|
23
|
+
<script>
|
24
|
+
function connect({ client_addr, server_addr }) {
|
25
|
+
const client = new WebSocket(client_addr + "/client");
|
26
|
+
const server = new WebSocket(server_addr + "/server");
|
27
|
+
|
28
|
+
server.onerror = function(event) { console.error("server/error"); };
|
29
|
+
client.onerror = function(event) { console.error("client/error"); };
|
30
|
+
|
31
|
+
client.onopen = function() {
|
32
|
+
server.onopen = function() {
|
33
|
+
client.onmessage = function(msg) { server.send(msg.data); };
|
34
|
+
server.onmessage = function(msg) { client.send(msg.data); };
|
35
|
+
};
|
36
|
+
};
|
37
|
+
|
38
|
+
server.onclose = function(event) {
|
39
|
+
console.log("server/close", event.code, event.reason);
|
40
|
+
|
41
|
+
if (client.readyState === WebSocket.OPEN) client.close();
|
42
|
+
};
|
43
|
+
|
44
|
+
client.onclose = function(event) {
|
45
|
+
console.log("client/close", event.code, event.reason);
|
46
|
+
|
47
|
+
if (server.readyState === WebSocket.OPEN) server.close();
|
48
|
+
};
|
49
|
+
}
|
50
|
+
|
51
|
+
const url = new URL(location.href);
|
52
|
+
const client_addr = `ws://${location.hostname}:8000`;
|
53
|
+
const server_addr = url.searchParams.get("server") || "wss://weblinkapp.herokuapp.com";
|
54
|
+
const batch_size = Number(url.searchParams.get("batch")) || 4
|
55
|
+
const control = new WebSocket(client_addr + "/control");
|
56
|
+
|
57
|
+
control.onerror = function(event) { console.error("control/error"); };
|
58
|
+
control.onclose = function(event) { console.log("control/close"); };
|
59
|
+
control.onmessage = function(msg) {
|
60
|
+
for (var i = 0; i < batch_size; i++) {
|
61
|
+
connect({ client_addr, server_addr });
|
62
|
+
}
|
63
|
+
};
|
64
|
+
control.onmessage();
|
65
|
+
</script>
|
66
|
+
</body>
|
67
|
+
</html>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"background_color": "#3f9cff",
|
3
|
+
"display": "standalone",
|
4
|
+
"icons": [
|
5
|
+
{
|
6
|
+
"sizes": "192x192",
|
7
|
+
"src": "/android-chrome-192x192.png",
|
8
|
+
"type": "image/png"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"sizes": "512x512",
|
12
|
+
"src": "/android-chrome-512x512.png",
|
13
|
+
"type": "image/png"
|
14
|
+
}
|
15
|
+
],
|
16
|
+
"name": "weblink",
|
17
|
+
"short_name": "weblink",
|
18
|
+
"theme_color": "#3f9cff"
|
19
|
+
}
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weblink
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- soylent
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDGjCCAgKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9lZmly
|
14
|
+
L0RDPXNveWxlbnQwHhcNMjAwMzA3MDEzNzUwWhcNMjEwMzA3MDEzNzUwWjAaMRgw
|
15
|
+
FgYDVQQDDA9lZmlyL0RDPXNveWxlbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
16
|
+
ggEKAoIBAQC2DMbzgA39U+3VTMjXn+0jnOQyLdmXQ5EXgSLKCgBLIcFTc9J47Th0
|
17
|
+
7Yb/f4RzWh49/EkDBiDtLqFeKBYsj3q0e8tRCAs32NtVyl/4FDyJvWsK3R2tcXOV
|
18
|
+
qxs48J3CgG+rFLOcMC9YF4FPTkz4p3EYGFVjZTbiqyVVuIZzWtrwdZesBVgpBRyN
|
19
|
+
8sEyNoi8vcDiOmEwf9/TVMTDf/wu6a+i3LNVGYWlvgMJRssaAnj/IbFFtPTz30Hx
|
20
|
+
eTUdfJu8YbwRspfFzcJGLf32E7vXfmHHqNzqjh4zD9sVpvTHbqLLsgVa+nYHPHAe
|
21
|
+
dzSZ5gZjG1oZ7hZDCJoEPj0oCHT0qkuXAgMBAAGjazBpMAkGA1UdEwQCMAAwCwYD
|
22
|
+
VR0PBAQDAgSwMB0GA1UdDgQWBBSsvp1HAfA+QTjOg/ehyhv7adp0FjAXBgNVHREE
|
23
|
+
EDAOgQxlZmlyQHNveWxlbnQwFwYDVR0SBBAwDoEMZWZpckBzb3lsZW50MA0GCSqG
|
24
|
+
SIb3DQEBCwUAA4IBAQBsJ7htnm3RB5xQwzC2agRAgG2ax5uaD6lCEPWshGJbfT6v
|
25
|
+
jaRrwrPSbaxBTD2v8QpXJe/fALJKWHUbNZilZU2t7HyQkfSyVQyLYcjo7lWFoHA1
|
26
|
+
z0YB3dCGcMkvLa7r73ynEtYbnYfesbXVlcRJG7SgJqWvZMnj1HnYfBevlcjSBFy2
|
27
|
+
5xZKSwreHM+va8McxpEZmG3ecdefRQ1u+xZabamN2hhel3nKF1BUBqgxYWXYRkZP
|
28
|
+
VIAqJBK/gwFlRxVYjmi2w4Ouc4wL8HtX104yQqOuD9gVPN6PJecue66As7i2I/2q
|
29
|
+
EARmnubhy24GUdxEooHV6pOs1mLLRuFepMgBnHfs
|
30
|
+
-----END CERTIFICATE-----
|
31
|
+
date: 2020-12-23 00:00:00.000000000 Z
|
32
|
+
dependencies:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: em-websocket
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.5'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.5'
|
47
|
+
description:
|
48
|
+
email:
|
49
|
+
executables:
|
50
|
+
- weblink
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- CHANGELOG
|
55
|
+
- VERSION
|
56
|
+
- bin/weblink
|
57
|
+
- lib/relay.rb
|
58
|
+
- lib/weblink.rb
|
59
|
+
- public/android-chrome-192x192.png
|
60
|
+
- public/android-chrome-512x512.png
|
61
|
+
- public/apple-touch-icon.png
|
62
|
+
- public/favicon-16x16.png
|
63
|
+
- public/favicon-32x32.png
|
64
|
+
- public/favicon.ico
|
65
|
+
- public/index.html
|
66
|
+
- public/weblink.webmanifest
|
67
|
+
homepage: https://github.com/soylent/weblink
|
68
|
+
licenses: []
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.5.0
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubygems_version: 3.1.4
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Web browser gateway
|
89
|
+
test_files: []
|
metadata.gz.sig
ADDED