New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

chrome-remote-multiplex

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chrome-remote-multiplex - npm Package Compare versions

Comparing version 0.1.4 to 0.1.5

LICENSE

266

dist/multiplex.js

@@ -42,2 +42,4 @@ 'use strict';

var PACKAGE = require("../package.json");
var Logger = function () {

@@ -58,2 +60,7 @@ function Logger() {

}, {
key: 'info',
value: function info() {
this.log.apply(this, arguments);
}
}, {
key: 'error',

@@ -293,2 +300,10 @@ value: function error() {

_createClass(DevtoolsClient, [{
key: 'close',
value: function close() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
}
}, {
key: 'onMessageFromClient',

@@ -381,2 +396,30 @@ value: function onMessageFromClient(data) {

/**
* Closes the connection to the server and clients
*/
}, {
key: 'close',
value: function close() {
if (this.ws) {
var ws = this.ws;
this.ws = null;
ws.close();
this.devtoolsClients.forEach(function (client) {
return client.close();
});
this.emit("close");
}
}
/**
* Returns true if there are no devtools clients
*/
}, {
key: 'isUnused',
value: function isUnused() {
return this.devtoolsClients.length == 0;
}
/**
* Detaches a DevTools client

@@ -388,8 +431,13 @@ */

value: function detach(devtoolsClient) {
var _this7 = this;
for (var i = 0; i < this.devtoolsClients.length; i++) {
if (this.devtoolsClients[i] === devtoolsClient) {
this.devtoolsClients.splice(i, 1);
return;
break;
}
}
}this.target.numberOfClients = this.devtoolsClients.length;
if (!!this.ws && this.autoClose && this.devtoolsClients.length == 0) this.closeTarget().then(function () {
return _this7.close();
});
}

@@ -405,5 +453,22 @@

this.devtoolsClients.push(devtoolsClient);
this.target.numberOfClients = this.devtoolsClients.length;
}
/**
* Shutsdown the target
*/
}, {
key: 'closeTarget',
value: function closeTarget() {
var t = this;
return httpGet({
hostname: t.multiplexServer.options.remoteClientHostname,
port: t.multiplexServer.options.remoteClientPort,
path: "/json/close/" + t.target.id,
method: 'GET'
});
}
/**
* Upgrade request from express

@@ -550,3 +615,3 @@ */

var _this7 = _possibleConstructorReturn(this, (MultiplexServer.__proto__ || Object.getPrototypeOf(MultiplexServer)).call(this));
var _this8 = _possibleConstructorReturn(this, (MultiplexServer.__proto__ || Object.getPrototypeOf(MultiplexServer)).call(this));

@@ -565,3 +630,3 @@ options = options || {};

}
_this7.options = {
_this8.options = {
listenPort: options.listenPort || 9223,

@@ -571,4 +636,4 @@ remoteClientHostname: options.remoteClientHostname || "localhost",

};
_this7.options.remoteClient = _this7.options.remoteClientHostname + ":" + _this7.options.remoteClientPort;
return _this7;
_this8.options.remoteClient = _this8.options.remoteClientHostname + ":" + _this8.options.remoteClientPort;
return _this8;
}

@@ -579,2 +644,4 @@

value: function listen() {
var _this9 = this;
var t = this;

@@ -594,3 +661,3 @@

var template = Dot.template('<html><body>\n <h1>Headless proxy</h1>\n <ul>\n {{~ it.multiplex.targets :target }}\n <li>\n <a href="{{= it.url(target) }}">\n {{= target.title }}\n </a>\n </li>\n {{~}}\n </ul>\n</body></html>');
var template = Dot.template('<html><body>\n <h1>Headless proxy</h1>\n <ul>\n {{~ it.multiplex.targets :target }}\n <li>\n <a href="{{= it.url(target) }}">\n {{= it.title(target) }}\n </a>\n </li>\n {{~}}\n </ul>\n</body></html>');

@@ -604,2 +671,7 @@ app.get('/', function (req, res) {

return DEFAULT_DEVTOOLS_URL + target.webSocketDebuggerUrl.replace(/^ws:\/\//, "ws=/") + "&remoteFrontend=true";
},
title: function title(target) {
var str = target.title;
if (target.autoClose) str += " (set to auto-close)";
return str;
}

@@ -611,14 +683,117 @@ }));

/*
* JSON data showing the targets which can be connected to
*/
function getTargetList(req, res) {
function getContentType(response) {
var contentType = response.headers["content-type"];
if (contentType) {
var pos = contentType.indexOf(';');
contentType = contentType.substring(0, pos);
}
return contentType;
}
// Gets JSON from the remote server
function getJson(path) {
return httpGet({
hostname: t.options.remoteClientHostname,
port: t.options.remoteClientPort,
path: path,
method: 'GET'
}).then(function (obj) {
var contentType = getContentType(obj.response);
if (contentType !== "application/json") LOG.warn("Expecting JSON from " + path + " but found wrong content type: " + contentType);
try {
return JSON.parse(obj.data);
} catch (ex) {
LOG.warn("Cannot parse JSON returned from " + path);
return null;
}
});
}
// Gets JSON from the remote server and copies it to the client
function copyToClient(req, res) {
return httpGet({
hostname: t.options.remoteClientHostname,
port: t.options.remoteClientPort,
path: req.originalUrl,
method: 'GET'
}).then(function (obj) {
var contentType = getContentType(obj.response);
if (contentType) res.set("Content-Type", contentType);
res.send(obj.data);
});
}
// REST API: list targets
app.get(["/json", "/json/list"], function (req, res) {
t.refreshTargets().then(function () {
res.set('Content-Type', 'text/json');
res.set("Content-Type", "application/json");
res.send(JSON.stringify(t.targets, null, 2));
}).catch(reportHttpError.bind(this, req));
}
app.get('/json', getTargetList);
app.get('/json/list', getTargetList);
}).catch(reportHttpError.bind(_this9, req));
});
// REST API: create a new target
app.get('/json/new', function (req, res) {
return getJson("/json/new").then(function (target) {
if (target) target = t._addTarget(target);
res.set("Content-Type", "application/json");
res.send(JSON.stringify(target, null, 2));
});
});
// REST API: close a target
app.get('/json/close/*', function (req, res) {
return httpGet({
hostname: t.options.remoteClientHostname,
port: t.options.remoteClientPort,
path: req.originalUrl,
method: 'GET'
}).then(function (obj) {
var id = req.originalUrl.match(/\/json\/close\/(.*)$/)[1];
var proxy = t.proxies[id];
if (proxy) proxy.close();
var contentType = getContentType(obj.response);
if (contentType) res.set("Content-Type", contentType);
res.send(obj.data);
});
});
// REST API: auto-close a target
app.get('/json/auto-close/*', function (req, res) {
var id = req.originalUrl.match(/\/json\/auto-close\/(.*)$/)[1];
var proxy = t._proxies[id];
if (proxy) {
if (proxy.isUnused()) {
proxy.closeTarget().close();
LOG.info("Closing target " + id + " due to /json/auto-close");
res.send("Target is closing");
} else {
proxy.autoClose = true;
t.targetsById[id].autoClose = true;
LOG.info("Marking target " + id + " to auto close");
res.send("Target set to auto close");
}
} else {
var target = t.targetsById[id];
if (target) {
target.autoClose = true;
LOG.info("Marking target " + id + " to auto close after first use");
res.send("Target will close after first use");
} else res.status(500).send("Unrecognised target id " + id);
}
});
// REST API: get version numbers
app.get('/json/version', function (req, res) {
return getJson(req.originalUrl).then(function (json) {
json["Chrome-Remote-Multiplex-Version"] = PACKAGE.version;
res.set("Content-Type", "application/json");
res.send(JSON.stringify(json, null, 2));
});
});
app.get('/json/protocol', copyToClient);
app.get('/json/activate', copyToClient);
var webServer = Http.createServer(app);

@@ -665,2 +840,12 @@ var proxies = this._proxies = {};

});
proxy.on('close', function () {
delete t.targetsById[uuid];
for (var i = 0; i < t.targets.length; i++) {
if (t.targets[i].id === uuid) {
t.targets.splice(i, 1);
break;
}
}
});
if (target.autoClose) proxy.autoClose = true;
} else {

@@ -702,5 +887,5 @@ return proxy.upgrade(request, socket, head);

method: 'GET'
}).then(function (data) {
}).then(function (obj) {
var json = null;
if (!data) {
if (!obj.data) {
LOG.debug("No data received from " + t.options.remoteClient);

@@ -710,3 +895,3 @@ return;

try {
json = JSON.parse(data);
json = JSON.parse(obj.data);
} catch (ex) {

@@ -716,16 +901,37 @@ LOG.error("Error while parsing JSON from " + t.options.remoteClient + ": " + ex);

}
var oldTargets = t.targets;
var oldTargetsById = t.targetsById || {};
t.targets = json;
t.targetsById = {};
var regex = new RegExp("localhost:" + t.options.remoteClientPort);
json.forEach(function (target) {
if (target.devtoolsFrontendUrl) target.originalDevtoolsFrontendUrl = target.devtoolsFrontendUrl;
target.devtoolsFrontendUrl = "/devtools/inspector.html?ws=localhost:" + t.options.listenPort + "/devtools/page/" + target.id;
if (target.webSocketDebuggerUrl) target.originalWebSocketDebuggerUrl = target.webSocketDebuggerUrl;
target.webSocketDebuggerUrl = "ws://localhost:" + t.options.listenPort + "/devtools/page/" + target.id;
target.title += " (proxied)";
t.targetsById[target.id] = target;
});
for (var i = 0; i < t.targets.length; i++) {
var target = t.targets[i];
t.targets[i] = t._addTarget(target, oldTargetsById[target.id]);
}
});
}
/**
* Adds a target
*/
}, {
key: '_addTarget',
value: function _addTarget(src, target) {
var RESERVED_KEYWORDS = ["description", "id", "title", "type", "url", "devtoolsFrontendUrl", "webSocketDebuggerUrl", "originalDevtoolsFrontendUrl", "originalWebSocketDebuggerUrl"];
var t = this;
if (!target) target = {};
for (var name in src) {
target[name] = src[name];
}if (target.devtoolsFrontendUrl) target.originalDevtoolsFrontendUrl = target.devtoolsFrontendUrl;
target.devtoolsFrontendUrl = "/devtools/inspector.html?ws=localhost:" + t.options.listenPort + "/devtools/page/" + target.id;
if (target.webSocketDebuggerUrl) target.originalWebSocketDebuggerUrl = target.webSocketDebuggerUrl;
target.webSocketDebuggerUrl = "ws://localhost:" + t.options.listenPort + "/devtools/page/" + target.id;
target.title += " (proxied)";
if (target.numberOfClients === undefined) target.numberOfClients = 0;
return t.targetsById[target.id] = target;
}
}]);

@@ -753,3 +959,3 @@

response.on('end', function () {
resolve(str);
resolve({ data: str, response: response });
});

@@ -756,0 +962,0 @@ });

2

package.json
{
"name": "chrome-remote-multiplex",
"version": "0.1.4",
"version": "0.1.5",
"description": "Allows multiple Chrome DevTools Clients to connect to a single Remote Debugger (ie Chrome Headless) instance",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -6,7 +6,9 @@ # chrome-remote-multiplex

Google Chrome Headless (or any other Devtools Protocol implementation) only allows one client to control
it at any particular time; this means that if you have an application which uses https://github.com/cyrus-and/chrome-remote-interface
it at any particular time; this means that if you have an application which uses
[chrome-remote-interface](https://github.com/cyrus-and/chrome-remote-interface)
to operate the web page, you cannot debug that web page while it is being controlled by your application.
By using https://github.com/johnspackman/chrome-remote-multiplex you can work around this restriction, by connecting your app and your
debugger(s) to chrome-remote-multiplex and allowing it to handle the single connection to Chrome Headless.
By using [chrome-remote-multiplex](https://github.com/johnspackman/chrome-remote-multiplex) you can work
around this restriction, by connecting your app and your debugger(s) to chrome-remote-multiplex and allowing
it to handle the single connection to Chrome Headless.

@@ -31,2 +33,54 @@

## Managing Lifecycle - automatically closing Chrome Tabs
When instrumenting Chrome Headless, you will often create and close instances - for example, [chrome-remote-interface](https://github.com/cyrus-and/chrome-remote-interface)
has this example use of the command line to create a instance (i.e. a tab or window) and then close it again:
```
$ chrome-remote-interface new 'http://example.com'
{
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/b049bb56-de7d-424c-a331-6ae44cf7ae01",
"id": "b049bb56-de7d-424c-a331-6ae44cf7ae01",
"thumbnailUrl": "/thumb/b049bb56-de7d-424c-a331-6ae44cf7ae01",
"title": "",
"type": "page",
"url": "http://example.com/",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/b049bb56-de7d-424c-a331-6ae44cf7ae01"
}
$ chrome-remote-interface close 'b049bb56-de7d-424c-a331-6ae44cf7ae01'
```
Or as http requests, try this in your browser:
- `localhost:9222/json/new` -- output the new instance information
- `localhost:9222/json/close/{id}` -- where `{id}` is taken from the output of `/json/new`
If your application is running in a server environment, you obviously need to make sure that you keep track of all of the
instances that you create via the `new` command and make sure that you `close` them when they're no longer needed.
While this is straightforward to do in ideal circumstances, in a complex server application it can become tricky to manage
those instances, especially if you want to recover gracefully from application crashes or occasionally want to sneak in
with a separate connection and keep the the instance open while you debug it.
[chrome-remote-multiplex](https://github.com/johnspackman/chrome-remote-multiplex) adds an automatic close function that
tracks connections and when the last one has disconnected from an instance, the instance itself is closed down. This means
that even if your application crashes, the ab is cleaned up properly because the operating system will close the socket which
will disconnect from the MultiplexServer and then cause the tab to be removed also - this is garbage collection for your browser tabs.
To make a tab automatically close, use the new `/json/auto-close/{id}` API, for example:
```
$ chrome-remote-interface -p 9223 new 'http://www.google.co.uk'
# Let's say the output from the above command has an "id" of "b049bb56-de7d-424c-a331-6ae44cf7ae01"
$ # use the REST API to make the new tab auto-close
$ wget -O- http://localhost:9223/json/auto-close/b049bb56-de7d-424c-a331-6ae44cf7ae01
```
Now browse to `http://localhost:9223` and click on the link to start debugging your new tab; when you close that debugger and
go back to the `http://localhost:9223` you will see that the tab you just finished debugging has gone.
Note that if the instance has never been connected to, then it will only be closed once you have connected a DevTools client(s) to it
and the last client has disconnected; if you have previously connected and closed a DevTools client, the instance will close immmediately.
## Embedding in your application

@@ -50,3 +104,3 @@ You can embed the multiplex proxy server in your own application:

There is a full example in 'https://github.com/johnspackman/chrome-remote-multiplex/blob/master/example/embed.js'
There is a full example in [example/embed.js](https://github.com/johnspackman/chrome-remote-multiplex/blob/master/example/embed.js)

@@ -53,0 +107,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc