sf-static/test/node/lib/node_modules/net-browserify/browser.js
2023-02-28 19:21:07 +01:00

423 lines
11 KiB
JavaScript

var stream = require('stream');
var util = require('util');
var timers = require('timers');
var http = require('http');
var debug = util.debuglog('net');
var proxy = {
protocol: (window.location.protocol == 'https:') ? 'wss' : 'ws',
hostname: window.location.hostname,
port: window.location.port,
path: '/api/vm/net'
};
function getProxy() {
return proxy;
}
function getProxyHost() {
var host = getProxy().hostname;
if (getProxy().port) {
host += ':'+getProxy().port;
}
return host;
}
function getProxyOrigin() {
return getProxy().protocol + '://' + getProxyHost();
}
exports.setProxy = function (options) {
options = options || {};
if (options.protocol) {
proxy.protocol = options.protocol;
}
if (options.hostname) {
proxy.hostname = options.hostname;
}
if (options.port) {
proxy.port = options.port;
}
if (options.path) {
proxy.path = options.path;
}
};
exports.createServer = function () {
throw new Error('Cannot create server in a browser');
};
exports.connect = exports.createConnection = function (/* options, connectListener */) {
var args = normalizeConnectArgs(arguments);
debug('createConnection', args);
var s = new Socket(args[0]);
return Socket.prototype.connect.apply(s, args);
};
function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; }
function isPipeName(s) {
return util.isString(s) && toNumber(s) === false;
}
// Returns an array [options] or [options, cb]
// It is the same as the argument of Socket.prototype.connect().
function normalizeConnectArgs(args) {
var options = {};
if (util.isObject(args[0])) {
// connect(options, [cb])
options = args[0];
} else if (isPipeName(args[0])) {
// connect(path, [cb]);
options.path = args[0];
} else {
// connect(port, [host], [cb])
options.port = args[0];
if (util.isString(args[1])) {
options.host = args[1];
}
}
var cb = args[args.length - 1];
return util.isFunction(cb) ? [options, cb] : [options];
}
exports._normalizeConnectArgs = normalizeConnectArgs;
function Socket(options) {
if (!(this instanceof Socket)) return new Socket(options);
this._connecting = false;
this._host = null;
if (util.isNumber(options))
options = { fd: options }; // Legacy interface.
else if (util.isUndefined(options))
options = {};
stream.Duplex.call(this, options);
// these will be set once there is a connection
this.readable = this.writable = false;
// handle strings directly
this._writableState.decodeStrings = false;
// default to *not* allowing half open sockets
this.allowHalfOpen = options && options.allowHalfOpen || false;
}
util.inherits(Socket, stream.Duplex);
exports.Socket = Socket;
exports.Stream = Socket; // Legacy naming.
Socket.prototype.listen = function () {
throw new Error('Cannot listen in a browser');
};
Socket.prototype.setTimeout = function (msecs, callback) {
if (msecs > 0 && isFinite(msecs)) {
timers.enroll(this, msecs);
//timers._unrefActive(this);
if (callback) {
this.once('timeout', callback);
}
} else if (msecs === 0) {
timers.unenroll(this);
if (callback) {
this.removeListener('timeout', callback);
}
}
};
Socket.prototype._onTimeout = function () {
debug('_onTimeout');
this.emit('timeout');
};
Socket.prototype.setNoDelay = function (enable) {};
Socket.prototype.setKeepAlive = function (setting, msecs) {};
Socket.prototype.address = function () {
return {
address: this.remoteAddress,
port: this.remotePort,
family: this.remoteFamily
};
};
Object.defineProperty(Socket.prototype, 'readyState', {
get: function() {
if (this._connecting) {
return 'opening';
} else if (this.readable && this.writable) {
return 'open';
} else if (this.readable && !this.writable) {
return 'readOnly';
} else if (!this.readable && this.writable) {
return 'writeOnly';
} else {
return 'closed';
}
}
});
Socket.prototype.bufferSize = undefined;
Socket.prototype._read = function () {};
Socket.prototype.end = function(data, encoding) {
stream.Duplex.prototype.end.call(this, data, encoding);
this.writable = false;
if (this._ws) {
this._ws.close();
}
// just in case we're waiting for an EOF.
if (this.readable && !this._readableState.endEmitted)
this.read(0);
else
maybeDestroy(this);
};
// Call whenever we set writable=false or readable=false
function maybeDestroy(socket) {
if (!socket.readable &&
!socket.writable &&
!socket.destroyed &&
!socket._connecting &&
!socket._writableState.length) {
socket.destroy();
}
}
Socket.prototype.destroySoon = function() {
if (this.writable)
this.end();
if (this._writableState.finished)
this.destroy();
else
this.once('finish', this.destroy);
};
Socket.prototype.destroy = function(exception) {
debug('destroy', exception);
if (this.destroyed) {
return;
}
this._connecting = false;
this.readable = this.writable = false;
timers.unenroll(this);
debug('close');
this.destroyed = true;
};
Socket.prototype.remoteAddress = null;
Socket.prototype.remoteFamily = null;
Socket.prototype.remotePort = null;
// Used for servers only - not here
Socket.prototype.localAddress = null;
Socket.prototype.localPort = null;
Socket.prototype.bytesRead = 0;
Socket.prototype.bytesWritten = 0;
Socket.prototype._write = function (data, encoding, cb) {
var self = this;
cb = cb || function () {};
// If we are still connecting, then buffer this for later.
// The Writable logic will buffer up any more writes while
// waiting for this one to be done.
if (this._connecting) {
this._pendingData = data;
this._pendingEncoding = encoding;
this.once('connect', function() {
this._write(data, encoding, cb);
});
return;
}
this._pendingData = null;
this._pendingEncoding = '';
if (encoding == 'binary' && typeof data == 'string') { //TODO: maybe apply this for all string inputs?
// Setting encoding is very important for binary data - otherwise the data gets modified
data = new Buffer(data, encoding);
}
// Send the data
this._ws.send(data);
process.nextTick(function () {
//console.log('[tcp] sent: ', data.toString(), data.length);
self.bytesWritten += data.length;
cb();
});
};
Socket.prototype.write = function(chunk, encoding, cb) {
if (!util.isString(chunk) && !util.isBuffer(chunk))
throw new TypeError('invalid data');
return stream.Duplex.prototype.write.apply(this, arguments);
};
Socket.prototype.connect = function(options, cb) {
var self = this;
if (!util.isObject(options)) {
// Old API:
// connect(port, [host], [cb])
// connect(path, [cb]);
var args = normalizeConnectArgs(arguments);
return Socket.prototype.connect.apply(this, args);
}
cb = cb || function () {};
if (this.write !== Socket.prototype.write)
this.write = Socket.prototype.write;
if (options.path) {
throw new Error('options.path not supported in the browser');
}
self._connecting = true;
self.writable = true;
self._host = options.host;
var req = http.request({
hostname: getProxy().hostname,
port: getProxy().port,
path: getProxy().path + '/connect',
method: 'POST'
}, function (res) {
var json = '';
res.on('data', function (buf) {
json += buf;
});
res.on('end', function () {
var data = null;
try {
data = JSON.parse(json);
} catch (e) {
data = {
code: res.statusCode,
error: json
};
}
if (data.error) {
self.emit('error', 'Cannot open TCP connection ['+res.statusCode+']: '+data.error);
self.destroy();
return;
}
self.remoteAddress = data.remote.address;
self.remoteFamily = data.remote.family;
self.remotePort = data.remote.port;
self._connectWebSocket(data.token, function (err) {
if (err) {
cb(err);
return;
}
cb();
});
});
});
req.setHeader('Content-Type', 'application/json');
req.write(JSON.stringify(options));
req.end();
return this;
};
Socket.prototype._connectWebSocket = function (token, cb) {
var self = this;
if (self._ws) {
process.nextTick(function () {
cb();
});
return;
}
this._ws = new WebSocket(getProxyOrigin() + getProxy().path + '/socket?token='+token);
this._handleWebsocket();
if (cb) {
self.on('connect', cb);
}
};
Socket.prototype._handleWebsocket = function () {
var self = this;
this._ws.addEventListener('open', function () {
//console.log('TCP OK');
self._connecting = false;
self.readable = true;
self.emit('connect');
self.read(0);
});
this._ws.addEventListener('error', function (e) {
// `e` doesn't contain anything useful (https://developer.mozilla.org/en/docs/WebSockets/Writing_WebSocket_client_applications#Connection_errors)
console.warn('TCP error', e);
self.emit('error', 'An error occured with the WebSocket');
});
this._ws.addEventListener('message', function (e) {
var contents = e.data;
var gotBuffer = function (buffer) {
//console.log('[tcp] received: ' + buffer.toString(), buffer.length);
self.bytesRead += buffer.length;
self.push(buffer);
};
if (typeof contents == 'string') {
var buffer = new Buffer(contents);
gotBuffer(buffer);
} else if (window.Blob && contents instanceof Blob) {
var fileReader = new FileReader();
fileReader.addEventListener('load', function (e) {
var buf = fileReader.result;
var arr = new Uint8Array(buf);
gotBuffer(new Buffer(arr));
});
fileReader.readAsArrayBuffer(contents);
} else {
console.warn('Cannot read TCP stream: unsupported message type', contents);
}
});
this._ws.addEventListener('close', function () {
if (self.readyState == 'open') {
//console.log('TCP closed');
self.destroy();
}
});
};
exports.isIP = function (input) {
if (exports.isIPv4(input)) {
return 4;
} else if (exports.isIPv6(input)) {
return 6;
} else {
return 0;
}
};
exports.isIPv4 = function(input) {
return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(input);
};
exports.isIPv6 = function(input) {
return /^(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))$/.test(input);
};