purr 0.1.0 → 0.1.1
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 +5 -5
- data/.rspec +1 -1
- data/README.md +3 -39
- data/lib/purr/server.rb +32 -7
- data/lib/purr/version.rb +2 -2
- data/purr.gemspec +1 -1
- metadata +5 -15
- data/.travis.yml +0 -8
- data/contrib/chrome/_locales/en/messages.json +0 -1
- data/contrib/chrome/images/icon-128.png +0 -0
- data/contrib/chrome/images/icon-16.png +0 -0
- data/contrib/chrome/index.html +0 -34
- data/contrib/chrome/manifest.json +0 -35
- data/contrib/chrome/scripts/background.js +0 -26
- data/contrib/chrome/scripts/main.js +0 -178
- data/contrib/chrome/styles/main.css +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e3a99873499dacf72389b46731532a26976bebeb3deae0f3bd0c244648f9993f
|
4
|
+
data.tar.gz: e0e21c759cd247edaf3fea274fb5c060ea144e8844587abe20be81bba3e00a38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9026cd13d3a65e47a3b9d9702e8142cd196b96ef896026f9bcf4452ad66b5d8912026bee050f46a70d58e65eca827ec5117af83b0f182db7740d40613b8589f0
|
7
|
+
data.tar.gz: c6d439bba854c136237198a1a2b455d0ec516d653658fd5198d2c5d663840c77691a67a3aa9927649f2e528906da216f563eb8f4f2ad12da91ef0b2252a71ba5
|
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
--format
|
1
|
+
--format progress
|
2
2
|
--color
|
data/README.md
CHANGED
@@ -1,27 +1,9 @@
|
|
1
|
-
# Purr
|
1
|
+
# Purr Server
|
2
2
|
|
3
|
-
|
4
|
-
[](https://gemnasium.com/skateman/purr)
|
5
|
-
[](http://inch-ci.org/github/skateman/purr)
|
6
|
-
[](https://codeclimate.com/github/skateman/purr)
|
7
|
-
[](https://codecov.io/gh/skateman/purr)
|
8
|
-
|
9
|
-
Purr is a TCP-over-HTTP solution which consists:
|
10
|
-
- a Rack-based web server implemented in Ruby
|
11
|
-
- a browser extension implemented in ES6 using Chrome App JavaScript API
|
12
|
-
|
13
|
-
Using Purr it's possible to "smuggle" any kind of TCP traffic (SSH, VNC, etc.) through an HTTP connection.
|
14
|
-
|
15
|
-
**Note: this is a highly experimental implementation for demonstration purposes only!**
|
16
|
-
|
17
|
-
## How it works
|
18
|
-
|
19
|
-
**TODO**
|
3
|
+
This is the server part of Purr responsible for socket hijacking and forwarding TCP traffic to the remote endpoint.
|
20
4
|
|
21
5
|
## Installation
|
22
6
|
|
23
|
-
### Server
|
24
|
-
|
25
7
|
Add this line to your application's Gemfile:
|
26
8
|
|
27
9
|
```ruby
|
@@ -36,13 +18,8 @@ Or install it yourself as:
|
|
36
18
|
|
37
19
|
$ gem install purr
|
38
20
|
|
39
|
-
### Client
|
40
|
-
|
41
|
-
Currently, the client is available as a Chrome App only and it requires manual installation. It is available under the `contrib/chrome` folder and it needs to be installed manually. Note that the Chrome Apps will be [retired](https://blog.chromium.org/2016/08/from-chrome-apps-to-web.html) on other platforms than ChromeOS and this client might get unsupported in future versions of Chrome. It is planned to implement the client using a different approach in the future.
|
42
|
-
|
43
21
|
## Usage
|
44
22
|
|
45
|
-
### Server
|
46
23
|
The server needs to be wrapped as a Rack application and it's necessary to pass a block that takes one argument. This block should implement the TCP remote endpoint selection based on the **env** variable passed from the Rack context. The endpoint should be in the form of a two element array containing the host as a string and the port as an integer. There is a basic logging support implemented, but it is requires the `Rack::Logger` middleware to be included.
|
47
24
|
|
48
25
|
```ruby
|
@@ -69,17 +46,6 @@ rackup purr.ru
|
|
69
46
|
|
70
47
|
Note that the application requires a web server with [socket hijacking](http://www.rubydoc.info/github/rack/rack/file/SPEC#Hijacking) support, i.e. you can't use WEBrick.
|
71
48
|
|
72
|
-
### Client
|
73
|
-
The client can be invoked by pointing your browser to an URL in the form: `http://purr/<URL>`
|
74
|
-
|
75
|
-
Where the `<URL>` is an URL encoded using [`encodeURIComponent`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) pointing to the server described above.
|
76
|
-
|
77
|
-
Because the client catches the URL, it will never appear in the browser's address bar. Therefore, it is not recommended to use `window.open` or `window.location.href` for invoking the client as it will create an empty window. A better solution is to use `window.location.assign` from and existing window with "useful data":
|
78
|
-
|
79
|
-
```js
|
80
|
-
window.location.assign(`http://purr/${encodeURIComponent('http://example.com/vnc?id=1234')}`)
|
81
|
-
```
|
82
|
-
|
83
49
|
### Reverse proxy support
|
84
50
|
|
85
51
|
- TODO: Describe websocket compatibility mode and how it works with Apache mod_proxy_wstunnel
|
@@ -87,7 +53,7 @@ window.location.assign(`http://purr/${encodeURIComponent('http://example.com/vnc
|
|
87
53
|
|
88
54
|
## Development
|
89
55
|
|
90
|
-
After checking out the repo, run `bin/setup`
|
56
|
+
After checking out the repo, run `bin/setup` in the `server` subdirectory to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
91
57
|
|
92
58
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
93
59
|
|
@@ -95,8 +61,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
95
61
|
|
96
62
|
Bug reports and pull requests are welcome on GitHub at https://github.com/skateman/purr.
|
97
63
|
|
98
|
-
|
99
64
|
## License
|
100
65
|
|
101
66
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
102
|
-
|
data/lib/purr/server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "irb"
|
2
|
+
require 'socket'
|
1
3
|
require 'surro-gate'
|
2
4
|
|
3
5
|
module Purr
|
@@ -15,6 +17,24 @@ module Purr
|
|
15
17
|
|
16
18
|
@remote = block
|
17
19
|
@proxy = SurroGate.new
|
20
|
+
|
21
|
+
@transmitter = Thread.new do
|
22
|
+
loop do
|
23
|
+
@proxy.select(1000)
|
24
|
+
|
25
|
+
@proxy.each_ready do |left, right|
|
26
|
+
begin
|
27
|
+
right.write_nonblock(left.read_nonblock(4096))
|
28
|
+
rescue => ex
|
29
|
+
# FIXME: env is not available here, logging is probably bad in this way
|
30
|
+
# logger(env, :info, "Connection #{left} <-> #{right} closed due to #{ex}")
|
31
|
+
cleanup(left, right)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
@transmitter.abort_on_exception = true
|
18
38
|
end
|
19
39
|
|
20
40
|
# Method required by the Rack API
|
@@ -47,11 +67,8 @@ module Purr
|
|
47
67
|
return [200, {}, []]
|
48
68
|
rescue => ex
|
49
69
|
logger(env, :error, "#{ex.class} happened for #{env['REMOTE_ADDR']} trying to access #{host}:#{port}")
|
50
|
-
|
51
|
-
|
52
|
-
sock.close unless sock.nil? || sock.closed?
|
53
|
-
# Return with a 404 error
|
54
|
-
return not_found
|
70
|
+
cleanup(http, sock)
|
71
|
+
return not_found # Return with a 404 error
|
55
72
|
end
|
56
73
|
|
57
74
|
private
|
@@ -74,8 +91,8 @@ module Purr
|
|
74
91
|
<<~HEREDOC.sub(/\n$/, "\n\n").gsub(/ {2,}/, '').gsub("\n", "\r\n")
|
75
92
|
HTTP/1.1 101 Switching Protocols
|
76
93
|
Upgrade: #{upgrade}
|
77
|
-
|
78
|
-
|
94
|
+
Purr-Version: #{Purr::VERSION}
|
95
|
+
Purr-Request: MEOW
|
79
96
|
Connection: Upgrade
|
80
97
|
HEREDOC
|
81
98
|
end
|
@@ -84,6 +101,14 @@ module Purr
|
|
84
101
|
[404, { 'Content-Type' => 'text/plain' }, ['Not found!']]
|
85
102
|
end
|
86
103
|
|
104
|
+
def cleanup(*sockets)
|
105
|
+
# Omit `nil`s from the array
|
106
|
+
sockets.compact!
|
107
|
+
# Close the opened sockets and remove them from the proxy
|
108
|
+
sockets.each { |sock| sock.close unless sock.closed? }
|
109
|
+
@proxy.pop(*sockets)
|
110
|
+
end
|
111
|
+
|
87
112
|
def logger(env, level, message)
|
88
113
|
# Do logging only if Rack::Logger is loaded as a middleware
|
89
114
|
env['rack.logger'].send(level, message) if env['rack.logger']
|
data/lib/purr/version.rb
CHANGED
data/purr.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ['lib']
|
23
23
|
|
24
24
|
spec.add_dependency 'rack', '~> 2.0.0'
|
25
|
-
spec.add_dependency 'surro-gate', '~> 0.
|
25
|
+
spec.add_dependency 'surro-gate', '~> 1.0.4'
|
26
26
|
|
27
27
|
spec.add_development_dependency 'bundler', '~> 1.13'
|
28
28
|
spec.add_development_dependency 'codecov', '~> 0.1.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: purr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dávid Halász
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 1.0.4
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
40
|
+
version: 1.0.4
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,21 +118,12 @@ files:
|
|
118
118
|
- ".gitignore"
|
119
119
|
- ".rspec"
|
120
120
|
- ".rubocop.yml"
|
121
|
-
- ".travis.yml"
|
122
121
|
- Gemfile
|
123
122
|
- LICENSE.txt
|
124
123
|
- README.md
|
125
124
|
- Rakefile
|
126
125
|
- bin/console
|
127
126
|
- bin/setup
|
128
|
-
- contrib/chrome/_locales/en/messages.json
|
129
|
-
- contrib/chrome/images/icon-128.png
|
130
|
-
- contrib/chrome/images/icon-16.png
|
131
|
-
- contrib/chrome/index.html
|
132
|
-
- contrib/chrome/manifest.json
|
133
|
-
- contrib/chrome/scripts/background.js
|
134
|
-
- contrib/chrome/scripts/main.js
|
135
|
-
- contrib/chrome/styles/main.css
|
136
127
|
- lib/purr.rb
|
137
128
|
- lib/purr/server.rb
|
138
129
|
- lib/purr/version.rb
|
@@ -156,8 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
156
147
|
- !ruby/object:Gem::Version
|
157
148
|
version: '0'
|
158
149
|
requirements: []
|
159
|
-
|
160
|
-
rubygems_version: 2.5.1
|
150
|
+
rubygems_version: 3.0.2
|
161
151
|
signing_key:
|
162
152
|
specification_version: 4
|
163
153
|
summary: Smuggle TCP connections through HTTP
|
data/.travis.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{}
|
Binary file
|
Binary file
|
data/contrib/chrome/index.html
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
<!doctype html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<meta charset="utf-8">
|
5
|
-
<title>Purr Client</title>
|
6
|
-
<link rel="stylesheet" type="text/css" href="styles/main.css"/>
|
7
|
-
</head>
|
8
|
-
<body>
|
9
|
-
<div class="container">
|
10
|
-
<h1>Purr Client v<span class="version"></span></h1>
|
11
|
-
<div class="info-messages">
|
12
|
-
<p class="init">
|
13
|
-
Initializing connection, please wait...
|
14
|
-
</p>
|
15
|
-
<p class="work hidden">
|
16
|
-
Listening on: <strong class="address" title="Click to copy!"></strong><br/>
|
17
|
-
Connected clients: <span class="clients">0</span>
|
18
|
-
</p>
|
19
|
-
<p class="error hidden">
|
20
|
-
<strong>Error:</strong> <span class="errmsg"></span>
|
21
|
-
</p>
|
22
|
-
</div>
|
23
|
-
<div class="settings">
|
24
|
-
<label>Compatibility:</label>
|
25
|
-
<select id="websocket">
|
26
|
-
<option value="purr">None</option>
|
27
|
-
<option value="websocket">Fake WebSocket</option>
|
28
|
-
<!-- <option value="">Full WebSocket</option> -->
|
29
|
-
</select>
|
30
|
-
</div>
|
31
|
-
</div>
|
32
|
-
<script src="scripts/main.js"></script>
|
33
|
-
</body>
|
34
|
-
</html>
|
@@ -1,35 +0,0 @@
|
|
1
|
-
{
|
2
|
-
"name": "Purr Client",
|
3
|
-
"description": "Smuggle TCP connections through HTTP",
|
4
|
-
"default_locale": "en",
|
5
|
-
"version": "0.1.0",
|
6
|
-
"manifest_version": 2,
|
7
|
-
"sockets": {
|
8
|
-
"tcp": {
|
9
|
-
"connect": "*"
|
10
|
-
},
|
11
|
-
"tcpServer": {
|
12
|
-
"listen": "*"
|
13
|
-
}
|
14
|
-
},
|
15
|
-
"url_handlers": {
|
16
|
-
"launch_proxy": {
|
17
|
-
"matches": [
|
18
|
-
"http://purr/*",
|
19
|
-
"https://purr/*"
|
20
|
-
],
|
21
|
-
"title": "Launch Purr Client"
|
22
|
-
}
|
23
|
-
},
|
24
|
-
"icons": {
|
25
|
-
"16": "images/icon-16.png",
|
26
|
-
"128": "images/icon-128.png"
|
27
|
-
},
|
28
|
-
"app": {
|
29
|
-
"background": {
|
30
|
-
"scripts": [
|
31
|
-
"scripts/background.js"
|
32
|
-
]
|
33
|
-
}
|
34
|
-
}
|
35
|
-
}
|
@@ -1,26 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
|
3
|
-
chrome.app.runtime.onLaunched.addListener((data) => {
|
4
|
-
// Pass the url for further processing
|
5
|
-
window.url = data.url;
|
6
|
-
// Create the application window
|
7
|
-
chrome.app.window.create('index.html', {
|
8
|
-
innerBounds: {
|
9
|
-
width: 400,
|
10
|
-
height: 160
|
11
|
-
}
|
12
|
-
}, (win) =>
|
13
|
-
// Clean up all sockets if the app window gets closed
|
14
|
-
win.onClosed.addListener(() =>
|
15
|
-
['tcp', 'tcpServer'].forEach((provider) =>
|
16
|
-
chrome.sockets[provider].getSockets((sockets) =>
|
17
|
-
sockets.forEach((socket) => {
|
18
|
-
chrome.sockets[provider].disconnect(socket.socketId);
|
19
|
-
chrome.sockets[provider].close(socket.socketId);
|
20
|
-
})
|
21
|
-
)
|
22
|
-
)
|
23
|
-
)
|
24
|
-
);
|
25
|
-
});
|
26
|
-
|
@@ -1,178 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
|
3
|
-
const showError = (msg) => {
|
4
|
-
document.querySelector('.error > .errmsg').textContent = msg;
|
5
|
-
showInfo('error');
|
6
|
-
};
|
7
|
-
|
8
|
-
const showInfo = (klass) => {
|
9
|
-
if (['init', 'work', 'error'].includes(klass)) {
|
10
|
-
document.querySelector(`.info-messages > :not(.hidden)`).classList.add('hidden');
|
11
|
-
document.querySelector(`.info-messages .${klass}`).classList.remove('hidden');
|
12
|
-
}
|
13
|
-
};
|
14
|
-
|
15
|
-
const copyToClipboard = (e) => {
|
16
|
-
var input = document.createElement('textarea');
|
17
|
-
document.body.appendChild(input);
|
18
|
-
input.value = e.target.textContent;
|
19
|
-
input.focus();
|
20
|
-
input.select();
|
21
|
-
document.execCommand('Copy');
|
22
|
-
input.remove();
|
23
|
-
};
|
24
|
-
|
25
|
-
const sendHttpRequest = (sock) => {
|
26
|
-
// Build up the HTTP request headers
|
27
|
-
const upgrade = document.querySelector('#websocket').value;
|
28
|
-
|
29
|
-
const req = `
|
30
|
-
GET ${window.request.path} HTTP/1.1 \r
|
31
|
-
Host: ${window.request.host} \r
|
32
|
-
Upgrade: ${upgrade} \r
|
33
|
-
Purr-Request: MEOW \r
|
34
|
-
Purr-Version: ${chrome.runtime.getManifest().version}\r
|
35
|
-
Connection: Upgrade \r
|
36
|
-
User-Agent: ${navigator.userAgent} \r
|
37
|
-
`.replace(/( {2,})|(^\n)/g, '').replace(/\r\n$/, '\r\n\r\n');
|
38
|
-
|
39
|
-
let buffer = new ArrayBuffer(req.length);
|
40
|
-
let writer = new Uint8Array(buffer);
|
41
|
-
// Convert it to the appropriate format
|
42
|
-
for (let i=0, len=req.length; i<len; i++) {
|
43
|
-
writer[i] = req.charCodeAt(i);
|
44
|
-
}
|
45
|
-
|
46
|
-
// Send it out
|
47
|
-
chrome.sockets.tcp.send(sock, buffer, () => null);
|
48
|
-
};
|
49
|
-
|
50
|
-
const windowLoaded = () => new Promise((resolve, reject) =>
|
51
|
-
document.addEventListener('DOMContentLoaded', () => {
|
52
|
-
document.querySelector('span.version').textContent = chrome.runtime.getManifest().version;
|
53
|
-
resolve();
|
54
|
-
})
|
55
|
-
);
|
56
|
-
|
57
|
-
const parseBackgroundURL = () => new Promise((resolve, reject) =>
|
58
|
-
chrome.runtime.getBackgroundPage((background) => {
|
59
|
-
if (background.url) {
|
60
|
-
var url = new URL(decodeURIComponent(background.url.replace(/^https?:\/\/purr\// ,'')));
|
61
|
-
var m = url.protocol.match(/^http(s?):$/);
|
62
|
-
|
63
|
-
if (m) {
|
64
|
-
window.request = {
|
65
|
-
hostname: url.hostname,
|
66
|
-
host: url.host,
|
67
|
-
port: url.port ? parseInt(url.port) : (m[1] ? 443 : 80),
|
68
|
-
path: url.pathname,
|
69
|
-
secure: !!m[1]
|
70
|
-
};
|
71
|
-
resolve();
|
72
|
-
} else {
|
73
|
-
reject('Invalid URL!');
|
74
|
-
}
|
75
|
-
} else {
|
76
|
-
reject('This application cannot be started separately!');
|
77
|
-
}
|
78
|
-
})
|
79
|
-
);
|
80
|
-
|
81
|
-
const createServer = () => new Promise((resolve, reject) =>
|
82
|
-
chrome.sockets.tcpServer.create({}, (server) =>
|
83
|
-
chrome.sockets.tcpServer.listen(server.socketId, '127.0.0.1', 8888, 0, (result) =>
|
84
|
-
chrome.sockets.tcpServer.getInfo(server.socketId, (info) => {
|
85
|
-
if (result < 0) {
|
86
|
-
reject(`tcpServer.listen returned with ${result}`);
|
87
|
-
} else {
|
88
|
-
document.querySelector('.address').textContent = `127.0.0.1:${info.localPort}`;
|
89
|
-
document.querySelector('.address').onclick = copyToClipboard;
|
90
|
-
showInfo('work');
|
91
|
-
resolve(server.socketId);
|
92
|
-
}
|
93
|
-
})
|
94
|
-
)
|
95
|
-
)
|
96
|
-
);
|
97
|
-
|
98
|
-
const setListeners = (promise) => {
|
99
|
-
// Set up the proxying
|
100
|
-
chrome.sockets.tcp.onReceive.addListener((recv) => {
|
101
|
-
let node = window.pairing[recv.socketId];
|
102
|
-
if (node.purr) { // Synchronize
|
103
|
-
// Convert the response to a readable format
|
104
|
-
let response = String.fromCharCode.apply(null, new Uint8Array(recv.data));
|
105
|
-
|
106
|
-
if (response.match(/^HTTP\/1\.1 101 Switching Protocols/)) {
|
107
|
-
// If the upgrade was successful, unpause the socket
|
108
|
-
chrome.sockets.tcp.setPaused(node.pair, false, () =>
|
109
|
-
delete node.purr
|
110
|
-
);
|
111
|
-
} else {
|
112
|
-
// The upgrade was not successful, close the connection
|
113
|
-
console.error('Error happened during HTTP upgrade...')
|
114
|
-
cleanupClient(recv.socketId);
|
115
|
-
}
|
116
|
-
|
117
|
-
} else { // Transmit normally
|
118
|
-
chrome.sockets.tcp.send(node.pair, recv.data, () => null);
|
119
|
-
}
|
120
|
-
});
|
121
|
-
|
122
|
-
// Error handling for client connections
|
123
|
-
chrome.sockets.tcp.onReceiveError.addListener((err) =>
|
124
|
-
cleanupClient(err.socketId)
|
125
|
-
);
|
126
|
-
|
127
|
-
// Keeping up the promise-chain
|
128
|
-
return promise;
|
129
|
-
};
|
130
|
-
|
131
|
-
const createClient = (peer) => new Promise((resolve, reject) =>
|
132
|
-
chrome.sockets.tcp.create({}, (client) =>
|
133
|
-
chrome.sockets.tcp.connect(client.socketId, window.request.hostname, window.request.port, (result) => {
|
134
|
-
if (result < 0) {
|
135
|
-
reject(`tcp.connect returned with ${result}`);
|
136
|
-
} else {
|
137
|
-
// Set up socket pairing information
|
138
|
-
window.pairing[peer] = { pair: client.socketId };
|
139
|
-
window.pairing[client.socketId] = { pair: peer, purr: true };
|
140
|
-
updateClients();
|
141
|
-
resolve(client.socketId);
|
142
|
-
}
|
143
|
-
})
|
144
|
-
)
|
145
|
-
);
|
146
|
-
|
147
|
-
const cleanupClient = (sock) => {
|
148
|
-
let node = window.pairing[sock];
|
149
|
-
if (node) { // Do not close them twice
|
150
|
-
delete window.pairing[sock];
|
151
|
-
delete window.pairing[node.pair];
|
152
|
-
|
153
|
-
updateClients();
|
154
|
-
|
155
|
-
chrome.sockets.tcp.close(sock);
|
156
|
-
chrome.sockets.tcp.close(node.pair);
|
157
|
-
}
|
158
|
-
};
|
159
|
-
|
160
|
-
const acceptServer = (server) => {
|
161
|
-
chrome.sockets.tcpServer.onAccept.addListener((client) => {
|
162
|
-
createClient(client.clientSocketId).then(sendHttpRequest, showError);
|
163
|
-
return server;
|
164
|
-
})
|
165
|
-
};
|
166
|
-
|
167
|
-
const updateClients = () => {
|
168
|
-
document.querySelector('.clients').textContent = parseInt(Object.keys(pairing).length / 2);
|
169
|
-
};
|
170
|
-
|
171
|
-
window.pairing = {};
|
172
|
-
|
173
|
-
windowLoaded()
|
174
|
-
.then(parseBackgroundURL)
|
175
|
-
.then(createServer)
|
176
|
-
.then(setListeners)
|
177
|
-
.then(acceptServer)
|
178
|
-
.catch(showError);
|
@@ -1,20 +0,0 @@
|
|
1
|
-
strong.address:hover {
|
2
|
-
cursor: pointer;
|
3
|
-
text-decoration: underline;
|
4
|
-
}
|
5
|
-
|
6
|
-
.error {
|
7
|
-
color: red;
|
8
|
-
}
|
9
|
-
|
10
|
-
.hidden {
|
11
|
-
display: none;
|
12
|
-
}
|
13
|
-
|
14
|
-
.container {
|
15
|
-
text-align: center;
|
16
|
-
}
|
17
|
-
|
18
|
-
.info-messages {
|
19
|
-
font-size: larger;
|
20
|
-
}
|