UNPKG

rhea

Version:

reactive AMQP 1.0 library

1,452 lines (1,321 loc) 326 kB
require=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ (function (process,Buffer){(function (){ /* * Copyright 2015 Red Hat Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; var errors = require('./errors.js'); var frames = require('./frames.js'); var log = require('./log.js'); var sasl = require('./sasl.js'); var util = require('./util.js'); var EndpointState = require('./endpoint.js'); var Session = require('./session.js'); var Transport = require('./transport.js'); var fs = require('fs'); var os = require('os'); var path = require('path'); var net = require('net'); var tls = require('tls'); var EventEmitter = require('events').EventEmitter; var AMQP_PROTOCOL_ID = 0x00; function find_connect_config() { var paths; if (process.env.MESSAGING_CONNECT_FILE) { paths = [process.env.MESSAGING_CONNECT_FILE]; } else { paths = [process.cwd(), path.join(os.homedir(), '.config/messaging'),'/etc/messaging'].map(function (base) { return path.join(base, '/connect.json'); }); } for (var i = 0; i < paths.length; i++) { if (fs.existsSync(paths[i])) { var obj = JSON.parse(fs.readFileSync(paths[i], 'utf8')); log.config('using config from %s: %j', paths[i], obj); return obj; } } return {}; } function get_default_connect_config() { var config = find_connect_config(); var options = {}; if (config.scheme === 'amqps') options.transport = 'tls'; if (config.host) options.host = config.host; if(config.port === 'amqp') options.port = 5672; else if(config.port === 'amqps') options.port = 5671; else options.port = config.port; if (!(config.sasl && config.sasl.enabled === false)) { if (config.user) options.username = config.user; else options.username = 'anonymous'; if (config.password) options.password = config.password; if (config.sasl_mechanisms) options.sasl_mechanisms = config.sasl_mechanisms; } if (config.tls) { if (config.tls.key) options.key = fs.readFileSync(config.tls.key); if (config.tls.cert) options.cert = fs.readFileSync(config.tls.cert); if (config.tls.ca) options.ca = [ fs.readFileSync(config.tls.ca) ]; if (config.verify === false || config.tls.verify === false) options.rejectUnauthorized = false; } if (options.transport === 'tls') { options.servername = options.host; } return options; } function get_socket_id(socket) { if (socket.get_id_string) return socket.get_id_string(); return socket.localAddress + ':' + socket.localPort + ' -> ' + socket.remoteAddress + ':' + socket.remotePort; } function session_per_connection(conn) { var ssn = null; return { 'get_session' : function () { if (!ssn) { ssn = conn.create_session(); ssn.observers.on('session_close', function () { ssn = null; }); ssn.begin(); } return ssn; } }; } function restrict(count, f) { if (count) { var current = count; var reset; return function (successful_attempts) { if (reset !== successful_attempts) { current = count; reset = successful_attempts; } if (current--) return f(successful_attempts); else return -1; }; } else { return f; } } function backoff(initial, max) { var delay = initial; var reset; return function (successful_attempts) { if (reset !== successful_attempts) { delay = initial; reset = successful_attempts; } var current = delay; var next = delay*2; delay = max > next ? next : max; return current; }; } function get_connect_fn(options) { if (options.transport === undefined || options.transport === 'tcp') { return net.connect; } else if (options.transport === 'tls' || options.transport === 'ssl') { return tls.connect; } else { throw Error('Unrecognised transport: ' + options.transport); } } function connection_details(options) { var details = {}; details.connect = options.connect ? options.connect : get_connect_fn(options); details.host = options.host ? options.host : 'localhost'; details.port = options.port ? options.port : 5672; details.options = options; return details; } var aliases = [ 'container_id', 'hostname', 'max_frame_size', 'channel_max', 'idle_time_out', 'outgoing_locales', 'incoming_locales', 'offered_capabilities', 'desired_capabilities', 'properties' ]; function remote_property_shortcut(name) { return function() { return this.remote.open ? this.remote.open[name] : undefined; }; } function connection_fields(fields) { var o = {}; aliases.forEach(function (name) { if (fields[name] !== undefined) { o[name] = fields[name]; } }); return o; } function set_reconnect(reconnect, connection) { if (typeof reconnect === 'boolean') { if (reconnect) { var initial = connection.get_option('initial_reconnect_delay', 100); var max = connection.get_option('max_reconnect_delay', 60000); connection.options.reconnect = restrict( connection.get_option('reconnect_limit'), backoff(initial, max) ); } else { connection.options.reconnect = false; } } else if (typeof reconnect === 'number') { var fixed = connection.options.reconnect; connection.options.reconnect = restrict( connection.get_option('reconnect_limit'), function() { return fixed; } ); } } var conn_counter = 1; var Connection = function (options, container) { this.options = {}; if (options) { for (var k in options) { this.options[k] = options[k]; } if ((options.transport === 'tls' || options.transport === 'ssl') && options.servername === undefined && options.host !== undefined) { this.options.servername = options.host; } } else { this.options = get_default_connect_config(); } this.container = container; if (!this.options.id) { this.options.id = 'connection-' + conn_counter++; } if (!this.options.container_id) { this.options.container_id = container ? container.id : util.generate_uuid(); } if (!this.options.connection_details) { var self = this; this.options.connection_details = function() { return connection_details(self.options); }; } var reconnect = this.get_option('reconnect', true); set_reconnect(reconnect, this); this.registered = false; this.state = new EndpointState(); this.local_channel_map = {}; this.remote_channel_map = {}; this.local = {}; this.remote = {}; this.local.open = frames.open(connection_fields(this.options)); this.local.close = frames.close({}); this.session_policy = session_per_connection(this); this.amqp_transport = new Transport(this.options.id, AMQP_PROTOCOL_ID, frames.TYPE_AMQP, this); this.sasl_transport = undefined; this.transport = this.amqp_transport; this.conn_established_counter = 0; this.heartbeat_out = undefined; this.heartbeat_in = undefined; this.abort_idle = false; this.socket_ready = false; this.scheduled_reconnect = undefined; this.default_sender = undefined; this.closed_with_non_fatal_error = false; var self = this; aliases.forEach(function (alias) { Object.defineProperty(self, alias, { get: remote_property_shortcut(alias) }); }); Object.defineProperty(this, 'error', { get: function() { return this.remote.close ? this.remote.close.error : undefined; }}); }; Connection.prototype = Object.create(EventEmitter.prototype); Connection.prototype.constructor = Connection; Connection.prototype.dispatch = function(name) { log.events('[%s] Connection got event: %s', this.options.id, name); if (this.listeners(name).length) { EventEmitter.prototype.emit.apply(this, arguments); return true; } else if (this.container) { return this.container.dispatch.apply(this.container, arguments); } else { return false; } }; Connection.prototype._disconnect = function() { this.state.disconnected(); for (var k in this.local_channel_map) { this.local_channel_map[k]._disconnect(); } this.socket_ready = false; }; Connection.prototype._reconnect = function() { if (this.abort_idle) { this.abort_idle = false; this.local.close.error = undefined; this.state = new EndpointState(); this.state.open(); } this.state.reconnect(); this._reset_remote_state(); }; Connection.prototype._reset_remote_state = function() { //reset transport this.amqp_transport = new Transport(this.options.id, AMQP_PROTOCOL_ID, frames.TYPE_AMQP, this); this.sasl_transport = undefined; this.transport = this.amqp_transport; //reset remote endpoint state this.remote = {}; //reset sessions: this.remote_channel_map = {}; var localChannelMap = this.local_channel_map; for (var k in localChannelMap) { localChannelMap[k]._reconnect(); } }; Connection.prototype.connect = function () { this.is_server = false; this.abort_idle = false; this._reset_remote_state(); this._connect(this.options.connection_details(this.conn_established_counter)); this.open(); return this; }; Connection.prototype.reconnect = function () { this.scheduled_reconnect = undefined; log.reconnect('[%s] reconnecting...', this.options.id); this._reconnect(); this._connect(this.options.connection_details(this.conn_established_counter)); process.nextTick(this._process.bind(this)); return this; }; Connection.prototype.set_reconnect = function (reconnect) { set_reconnect(reconnect, this); }; Connection.prototype._connect = function (details) { if (details.connect) { this.init(details.connect(details.port, details.host, details.options, this.connected.bind(this))); } else { this.init(get_connect_fn(details)(details.port, details.host, details.options, this.connected.bind(this))); } return this; }; Connection.prototype.accept = function (socket) { this.is_server = true; log.io('[%s] client accepted: %s', this.id, get_socket_id(socket)); this.socket_ready = true; return this.init(socket); }; Connection.prototype.abort_socket = function (socket) { if (socket === this.socket) { log.io('[%s] aborting socket', this.options.id); this.socket.end(); if (this.socket.removeAllListeners) { this.socket.removeAllListeners('data'); this.socket.removeAllListeners('error'); this.socket.removeAllListeners('end'); } if (typeof this.socket.destroy === 'function') { this.socket.destroy(); } this._disconnected(); } }; Connection.prototype.init = function (socket) { this.socket = socket; if (this.get_option('tcp_no_delay', false) && this.socket.setNoDelay) { this.socket.setNoDelay(true); } this.socket.on('data', this.input.bind(this)); this.socket.on('error', this.on_error.bind(this)); this.socket.on('end', this.eof.bind(this)); if (this.is_server) { var mechs; if (this.container && Object.getOwnPropertyNames(this.container.sasl_server_mechanisms).length) { mechs = this.container.sasl_server_mechanisms; } if (this.socket.encrypted && this.socket.authorized && this.get_option('enable_sasl_external', false)) { mechs = sasl.server_add_external(mechs ? util.clone(mechs) : {}); } if (mechs) { if (mechs.ANONYMOUS !== undefined && !this.get_option('require_sasl', false)) { this.sasl_transport = new sasl.Selective(this, mechs); } else { this.sasl_transport = new sasl.Server(this, mechs); } } else { if (!this.get_option('disable_sasl', false)) { var anon = sasl.server_mechanisms(); anon.enable_anonymous(); this.sasl_transport = new sasl.Selective(this, anon); } } } else { var mechanisms = this.get_option('sasl_mechanisms'); if (!mechanisms) { var username = this.get_option('username'); var password = this.get_option('password'); var token = this.get_option('token'); if (username) { mechanisms = sasl.client_mechanisms(); if (password) mechanisms.enable_plain(username, password); else if (token) mechanisms.enable_xoauth2(username, token); else mechanisms.enable_anonymous(username); } } if (this.socket.encrypted && this.options.cert && this.get_option('enable_sasl_external', false)) { if (!mechanisms) mechanisms = sasl.client_mechanisms(); mechanisms.enable_external(); } if (mechanisms) { this.sasl_transport = new sasl.Client(this, mechanisms, this.options.sasl_init_hostname || this.options.servername || this.options.host); } } this.transport = this.sasl_transport ? this.sasl_transport : this.amqp_transport; return this; }; Connection.prototype.attach_sender = function (options) { return this.session_policy.get_session().attach_sender(options); }; Connection.prototype.open_sender = Connection.prototype.attach_sender;//alias Connection.prototype.attach_receiver = function (options) { if (this.get_option('tcp_no_delay', true) && this.socket.setNoDelay) { this.socket.setNoDelay(true); } return this.session_policy.get_session().attach_receiver(options); }; Connection.prototype.open_receiver = Connection.prototype.attach_receiver;//alias Connection.prototype.get_option = function (name, default_value) { if (this.options[name] !== undefined) return this.options[name]; else if (this.container) return this.container.get_option(name, default_value); else return default_value; }; Connection.prototype.send = function(msg) { if (this.default_sender === undefined) { this.default_sender = this.open_sender({target:{}}); } return this.default_sender.send(msg); }; Connection.prototype.connected = function () { this.socket_ready = true; this.conn_established_counter++; log.io('[%s] connected %s', this.options.id, get_socket_id(this.socket)); this.output(); }; Connection.prototype.sasl_failed = function (text, condition) { this.transport_error = new errors.ConnectionError(text, condition ? condition : 'amqp:unauthorized-access', this); this._handle_error(); this.socket.end(); }; Connection.prototype._is_fatal = function (error_condition) { var all_errors_non_fatal = this.get_option('all_errors_non_fatal', false); if (all_errors_non_fatal) { return false; } else { var non_fatal = this.get_option('non_fatal_errors', ['amqp:connection:forced']); return non_fatal.indexOf(error_condition) < 0; } }; Connection.prototype._handle_error = function () { var error = this.get_error(); if (error) { var handled = this.dispatch('connection_error', this._context({error:error})); handled = this.dispatch('connection_close', this._context({error:error})) || handled; if (!this._is_fatal(error.condition)) { if (this.state.local_open) { this.closed_with_non_fatal_error = true; } } else if (!handled) { this.dispatch('error', new errors.ConnectionError(error.description, error.condition, this)); } return true; } else { return false; } }; Connection.prototype.get_error = function () { if (this.transport_error) return this.transport_error; if (this.remote.close && this.remote.close.error) { return new errors.ConnectionError(this.remote.close.error.description, this.remote.close.error.condition, this); } return undefined; }; Connection.prototype._get_peer_details = function () { var s = ''; if (this.remote.open && this.remote.open.container) { s += this.remote.open.container + ' '; } if (this.remote.open && this.remote.open.properties) { s += JSON.stringify(this.remote.open.properties); } return s; }; Connection.prototype.output = function () { try { if (this.socket && this.socket_ready) { if (this.heartbeat_out) clearTimeout(this.heartbeat_out); this.transport.write(this.socket); if (((this.is_closed() && this.state.has_settled()) || this.abort_idle || this.transport_error) && !this.transport.has_writes_pending()) { this.socket.end(); } else if (this.is_open() && this.remote.open.idle_time_out) { this.heartbeat_out = setTimeout(this._write_frame.bind(this), this.remote.open.idle_time_out / 2); } if (this.local.open.idle_time_out && this.heartbeat_in === undefined) { this.heartbeat_in = setTimeout(this.idle.bind(this), this.local.open.idle_time_out); } } } catch (e) { this.saved_error = e; if (e.name === 'ProtocolError') { console.error('[' + this.options.id + '] error on write: ' + e + ' ' + this._get_peer_details() + ' ' + e.name); this.dispatch('protocol_error', e) || console.error('[' + this.options.id + '] error on write: ' + e + ' ' + this._get_peer_details()); } else { this.dispatch('error', e); } this.socket.end(); } }; function byte_to_hex(value) { if (value < 16) return '0x0' + Number(value).toString(16); else return '0x' + Number(value).toString(16); } function buffer_to_hex(buffer) { var bytes = []; for (var i = 0; i < buffer.length; i++) { bytes.push(byte_to_hex(buffer[i])); } return bytes.join(','); } Connection.prototype.input = function (buff) { var buffer; try { if (this.heartbeat_in) clearTimeout(this.heartbeat_in); log.io('[%s] read %d bytes', this.options.id, buff.length); if (this.previous_input) { buffer = Buffer.concat([this.previous_input, buff], this.previous_input.length + buff.length); this.previous_input = null; } else { buffer = buff; } var read = this.transport.read(buffer, this); if (read < buffer.length) { this.previous_input = buffer.slice(read); } if (this.local.open.idle_time_out) this.heartbeat_in = setTimeout(this.idle.bind(this), this.local.open.idle_time_out); if (this.transport.has_writes_pending()) { this.output(); } else if (this.is_closed() && this.state.has_settled()) { this.socket.end(); } else if (this.is_open() && this.remote.open.idle_time_out && !this.heartbeat_out) { this.heartbeat_out = setTimeout(this._write_frame.bind(this), this.remote.open.idle_time_out / 2); } } catch (e) { this.saved_error = e; if (e.name === 'ProtocolError') { this.dispatch('protocol_error', e) || console.error('[' + this.options.id + '] error on read: ' + e + ' ' + this._get_peer_details() + ' (buffer:' + buffer_to_hex(buffer) + ')'); } else { this.dispatch('error', e); } this.socket.end(); } }; Connection.prototype.idle = function () { if (!this.is_closed()) { this.abort_idle = true; this.closed_with_non_fatal_error = true; this.local.close.error = {condition:'amqp:resource-limit-exceeded', description:'max idle time exceeded'}; this.close(); setTimeout(this.abort_socket.bind(this, this.socket), 1000); } }; Connection.prototype.on_error = function (e) { this._disconnected(e); }; Connection.prototype.eof = function (e) { var error = e || this.saved_error; this.saved_error = undefined; this._disconnected(error); }; Connection.prototype._disconnected = function (error) { if (this.heartbeat_out) { clearTimeout(this.heartbeat_out); this.heartbeat_out = undefined; } if (this.heartbeat_in) { clearTimeout(this.heartbeat_in); this.heartbeat_in = undefined; } var was_closed_with_non_fatal_error = this.closed_with_non_fatal_error; if (this.closed_with_non_fatal_error) { this.closed_with_non_fatal_error = false; if (this.options.reconnect) this.open(); } if ((!this.is_closed() || was_closed_with_non_fatal_error) && this.scheduled_reconnect === undefined) { this._disconnect(); var disconnect_ctxt = {}; if (error) { disconnect_ctxt.error = error; } if (!this.is_server && !this.transport_error && this.options.reconnect) { var delay = this.options.reconnect(this.conn_established_counter); if (delay >= 0) { log.reconnect('[%s] Scheduled reconnect in ' + delay + 'ms', this.options.id); this.scheduled_reconnect = setTimeout(this.reconnect.bind(this), delay); disconnect_ctxt.reconnecting = true; } else { disconnect_ctxt.reconnecting = false; } } if (!this.dispatch('disconnected', this._context(disconnect_ctxt))) { console.warn('[' + this.options.id + '] disconnected %s', disconnect_ctxt.error || ''); } } }; Connection.prototype.open = function () { if (this.state.open()) { this._register(); } }; Connection.prototype.close = function (error) { if (error) this.local.close.error = error; if (this.state.close()) { this._register(); } }; Connection.prototype.is_open = function () { return this.state.is_open(); }; Connection.prototype.is_remote_open = function () { return this.state.remote_open; }; Connection.prototype.is_closed = function () { return this.state.is_closed(); }; Connection.prototype.create_session = function () { var i = 0; while (this.local_channel_map[i]) i++; var session = new Session(this, i); this.local_channel_map[i] = session; return session; }; Connection.prototype.find_sender = function (filter) { return this.find_link(util.sender_filter(filter)); }; Connection.prototype.find_receiver = function (filter) { return this.find_link(util.receiver_filter(filter)); }; Connection.prototype.find_link = function (filter) { for (var channel in this.local_channel_map) { var session = this.local_channel_map[channel]; var result = session.find_link(filter); if (result) return result; } return undefined; }; Connection.prototype.each_receiver = function (action, filter) { this.each_link(action, util.receiver_filter(filter)); }; Connection.prototype.each_sender = function (action, filter) { this.each_link(action, util.sender_filter(filter)); }; Connection.prototype.each_link = function (action, filter) { for (var channel in this.local_channel_map) { var session = this.local_channel_map[channel]; session.each_link(action, filter); } }; Connection.prototype.on_open = function (frame) { if (this.state.remote_opened()) { this.remote.open = frame.performative; this.open(); this.dispatch('connection_open', this._context()); } else { throw new errors.ProtocolError('Open already received'); } }; Connection.prototype.on_close = function (frame) { if (this.state.remote_closed()) { this.remote.close = frame.performative; if (this.remote.close.error) { this._handle_error(); } else { this.dispatch('connection_close', this._context()); } if (this.heartbeat_out) clearTimeout(this.heartbeat_out); var self = this; process.nextTick(function () { self.close(); }); } else { throw new errors.ProtocolError('Close already received'); } }; Connection.prototype._register = function () { if (!this.registered) { this.registered = true; process.nextTick(this._process.bind(this)); } }; Connection.prototype._process = function () { this.registered = false; do { if (this.state.need_open()) { this._write_open(); } var localChannelMap = this.local_channel_map; for (var k in localChannelMap) { localChannelMap[k]._process(); } if (this.state.need_close()) { this._write_close(); } } while (!this.state.has_settled()); }; Connection.prototype._write_frame = function (channel, frame, payload) { this.amqp_transport.encode(frames.amqp_frame(channel, frame, payload)); this.output(); }; Connection.prototype._write_open = function () { this._write_frame(0, this.local.open); }; Connection.prototype._write_close = function () { this._write_frame(0, this.local.close); }; Connection.prototype.on_begin = function (frame) { var session; if (frame.performative.remote_channel === null || frame.performative.remote_channel === undefined) { //peer initiated session = this.create_session(); session.local.begin.remote_channel = frame.channel; } else { session = this.local_channel_map[frame.performative.remote_channel]; if (!session) throw new errors.ProtocolError('Invalid value for remote channel ' + frame.performative.remote_channel); } session.on_begin(frame); this.remote_channel_map[frame.channel] = session; }; Connection.prototype.get_peer_certificate = function() { if (this.socket && this.socket.getPeerCertificate) { return this.socket.getPeerCertificate(); } else { return undefined; } }; Connection.prototype.get_tls_socket = function() { if (this.socket && (this.options.transport === 'tls' || this.options.transport === 'ssl')) { return this.socket; } else { return undefined; } }; Connection.prototype._context = function (c) { var context = c ? c : {}; context.connection = this; if (this.container) context.container = this.container; return context; }; Connection.prototype.remove_session = function (session) { if (this.remote_channel_map[session.remote.channel] === session) { delete this.remote_channel_map[session.remote.channel]; } if (this.local_channel_map[session.local.channel] === session) { delete this.local_channel_map[session.local.channel]; } }; Connection.prototype.remove_all_sessions = function () { clearObject(this.remote_channel_map); clearObject(this.local_channel_map); }; function clearObject(obj) { for (var k in obj) { if (!Object.prototype.hasOwnProperty.call(obj, k)) { continue; } delete obj[k]; } } function delegate_to_session(name) { Connection.prototype['on_' + name] = function (frame) { var session = this.remote_channel_map[frame.channel]; if (!session) { throw new errors.ProtocolError(name + ' received on invalid channel ' + frame.channel); } session['on_' + name](frame); }; } delegate_to_session('end'); delegate_to_session('attach'); delegate_to_session('detach'); delegate_to_session('transfer'); delegate_to_session('disposition'); delegate_to_session('flow'); module.exports = Connection; }).call(this)}).call(this,require('_process'),require("buffer").Buffer) },{"./endpoint.js":2,"./errors.js":3,"./frames.js":6,"./log.js":8,"./sasl.js":10,"./session.js":11,"./transport.js":13,"./util.js":15,"_process":43,"buffer":21,"events":27,"fs":20,"net":20,"os":41,"path":42,"tls":20}],2:[function(require,module,exports){ /* * Copyright 2015 Red Hat Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; var EndpointState = function () { this.init(); }; EndpointState.prototype.init = function () { this.local_open = false; this.remote_open = false; this.open_requests = 0; this.close_requests = 0; this.initialised = false; this.marker = undefined; }; EndpointState.prototype.mark = function (o) { this.marker = o || Date.now(); return this.marker; }; EndpointState.prototype.open = function () { this.marker = undefined; this.initialised = true; if (!this.local_open) { this.local_open = true; this.open_requests++; return true; } else { return false; } }; EndpointState.prototype.close = function () { this.marker = undefined; if (this.local_open) { this.local_open = false; this.close_requests++; return true; } else { return false; } }; EndpointState.prototype.disconnected = function () { var was_initialised = this.initialised; this.was_open = this.local_open; this.init(); this.initialised = was_initialised; }; EndpointState.prototype.reconnect = function () { if (this.was_open) { this.open(); this.was_open = undefined; } }; EndpointState.prototype.remote_opened = function () { if (!this.remote_open) { this.remote_open = true; return true; } else { return false; } }; EndpointState.prototype.remote_closed = function () { if (this.remote_open) { this.remote_open = false; return true; } else { return false; } }; EndpointState.prototype.is_open = function () { return this.local_open && this.remote_open; }; EndpointState.prototype.is_closed = function () { return this.initialised && !(this.local_open || this.was_open) && !this.remote_open; }; EndpointState.prototype.has_settled = function () { return this.open_requests === 0 && this.close_requests === 0; }; EndpointState.prototype.need_open = function () { if (this.open_requests > 0) { this.open_requests--; return true; } else { return false; } }; EndpointState.prototype.need_close = function () { if (this.close_requests > 0) { this.close_requests--; return true; } else { return false; } }; module.exports = EndpointState; },{}],3:[function(require,module,exports){ /* * Copyright 2015 Red Hat Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; var util = require('util'); function ProtocolError(message) { Error.call(this); this.message = message; this.name = 'ProtocolError'; } util.inherits(ProtocolError, Error); function TypeError(message) { ProtocolError.call(this, message); this.message = message; this.name = 'TypeError'; } util.inherits(TypeError, ProtocolError); function ConnectionError(message, condition, connection) { Error.call(this, message); this.message = message; this.name = 'ConnectionError'; this.condition = condition; this.description = message; Object.defineProperty(this, 'connection', { value: connection }); } util.inherits(ConnectionError, Error); ConnectionError.prototype.toJSON = function () { return { type: this.name, code: this.condition, message: this.description }; }; module.exports = { ProtocolError: ProtocolError, TypeError: TypeError, ConnectionError: ConnectionError }; },{"util":46}],4:[function(require,module,exports){ /* * Copyright 2018 Red Hat Inc. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an 'AS IS' BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; var ReceiverEvents; (function (ReceiverEvents) { /** * @property {string} message Raised when a message is received. */ ReceiverEvents['message'] = 'message'; /** * @property {string} receiverOpen Raised when the remote peer indicates the link is * open (i.e. attached in AMQP parlance). */ ReceiverEvents['receiverOpen'] = 'receiver_open'; /** * @property {string} receiverDrained Raised when the remote peer * indicates that it has drained all credit (and therefore there * are no more messages at present that it can send). */ ReceiverEvents['receiverDrained'] = 'receiver_drained'; /** * @property {string} receiverFlow Raised when a flow is received for receiver. */ ReceiverEvents['receiverFlow'] = 'receiver_flow'; /** * @property {string} receiverError Raised when the remote peer * closes the receiver with an error. The context may also have an * error property giving some information about the reason for the * error. */ ReceiverEvents['receiverError'] = 'receiver_error'; /** * @property {string} receiverClose Raised when the remote peer indicates the link is closed. */ ReceiverEvents['receiverClose'] = 'receiver_close'; /** * @property {string} settled Raised when the receiver link receives a disposition. */ ReceiverEvents['settled'] = 'settled'; })(ReceiverEvents || (ReceiverEvents = {})); var SenderEvents; (function (SenderEvents) { /** * @property {string} sendable Raised when the sender has sufficient credit to be able * to transmit messages to its peer. */ SenderEvents['sendable'] = 'sendable'; /** * @property {string} senderOpen Raised when the remote peer indicates the link is * open (i.e. attached in AMQP parlance). */ SenderEvents['senderOpen'] = 'sender_open'; /** * @property {string} senderDraining Raised when the remote peer * requests that the sender drain its credit; sending all * available messages within the credit limit and ensuring credit * is used up.. */ SenderEvents['senderDraining'] = 'sender_draining'; /** * @property {string} senderFlow Raised when a flow is received for sender. */ SenderEvents['senderFlow'] = 'sender_flow'; /** * @property {string} senderError Raised when the remote peer * closes the sender with an error. The context may also have an * error property giving some information about the reason for the * error. */ SenderEvents['senderError'] = 'sender_error'; /** * @property {string} senderClose Raised when the remote peer indicates the link is closed. */ SenderEvents['senderClose'] = 'sender_close'; /** * @property {string} accepted Raised when a sent message is accepted by the peer. */ SenderEvents['accepted'] = 'accepted'; /** * @property {string} released Raised when a sent message is released by the peer. */ SenderEvents['released'] = 'released'; /** * @property {string} rejected Raised when a sent message is rejected by the peer. */ SenderEvents['rejected'] = 'rejected'; /** * @property {string} modified Raised when a sent message is modified by the peer. */ SenderEvents['modified'] = 'modified'; /** * @property {string} settled Raised when the sender link receives a disposition. */ SenderEvents['settled'] = 'settled'; })(SenderEvents || (SenderEvents = {})); var SessionEvents; (function (SessionEvents) { /** * @property {string} sessionOpen Raised when the remote peer indicates the session is * open (i.e. attached in AMQP parlance). */ SessionEvents['sessionOpen'] = 'session_open'; /** * @property {string} sessionError Raised when the remote peer receives an error. The context * may also have an error property giving some information about the reason for the error. */ SessionEvents['sessionError'] = 'session_error'; /** * @property {string} sessionClose Raised when the remote peer indicates the session is closed. */ SessionEvents['sessionClose'] = 'session_close'; /** * @property {string} settled Raised when the session receives a disposition. */ SessionEvents['settled'] = 'settled'; })(SessionEvents || (SessionEvents = {})); var ConnectionEvents; (function (ConnectionEvents) { /** * @property {string} connectionOpen Raised when the remote peer indicates the connection is open. */ ConnectionEvents['connectionOpen'] = 'connection_open'; /** * @property {string} connectionClose Raised when the remote peer indicates the connection is closed. */ ConnectionEvents['connectionClose'] = 'connection_close'; /** * @property {string} connectionError Raised when the remote peer indicates an error occurred on * the connection. */ ConnectionEvents['connectionError'] = 'connection_error'; /** * @property {string} protocolError Raised when a protocol error is received on the underlying socket. */ ConnectionEvents['protocolError'] = 'protocol_error', /** * @property {string} error Raised when an error is received on the underlying socket. */ ConnectionEvents['error'] = 'error', /** * @property {string} disconnected Raised when the underlying tcp connection is lost. The context * has a reconnecting property which is true if the library is attempting to automatically reconnect * and false if it has reached the reconnect limit. If reconnect has not been enabled or if the connection * is a tcp server, then the reconnecting property is undefined. The context may also have an error * property giving some information about the reason for the disconnect. */ ConnectionEvents['disconnected'] = 'disconnected'; /** * @property {string} settled Raised when the connection receives a disposition. */ ConnectionEvents['settled'] = 'settled'; })(ConnectionEvents || (ConnectionEvents = {})); module.exports = { ReceiverEvents: ReceiverEvents, SenderEvents: SenderEvents, SessionEvents: SessionEvents, ConnectionEvents: ConnectionEvents }; },{}],5:[function(require,module,exports){ /* * Copyright 2015 Red Hat Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; var amqp_types = require('./types.js'); module.exports = { selector : function (s) { return {'jms-selector':amqp_types.wrap_described(s, 0x468C00000004)}; } }; },{"./types.js":14}],6:[function(require,module,exports){ /* * Copyright 2015 Red Hat Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; var types = require('./types.js'); var errors = require('./errors.js'); var frames = {}; var by_descriptor = {}; frames.read_header = function(buffer) { var offset = 4; var header = {}; var name = buffer.toString('ascii', 0, offset); if (name !== 'AMQP') { // output name in hex (null-bytes can be tough to deal with in ascii) throw new errors.ProtocolError('Invalid protocol header for AMQP: ' + buffer.toString('hex', 0, offset)); } header.protocol_id = buffer.readUInt8(offset++); header.major = buffer.readUInt8(offset++); header.minor = buffer.readUInt8(offset++); header.revision = buffer.readUInt8(offset++); //the protocol header is interpreted in different ways for //different versions(!); check some special cases to give clearer //error messages: if (header.protocol_id === 0 && header.major === 0 && header.minor === 9 && header.revision === 1) { throw new errors.ProtocolError('Unsupported AMQP version: 0-9-1'); } if (header.protocol_id === 1 && header.major === 1 && header.minor === 0 && header.revision === 10) { throw new errors.ProtocolError('Unsupported AMQP version: 0-10'); } if (header.major !== 1 || header.minor !== 0) { throw new errors.ProtocolError('Unsupported AMQP version: ' + JSON.stringify(header)); } return header; }; frames.write_header = function(buffer, header) { var offset = 4; buffer.write('AMQP', 0, offset, 'ascii'); buffer.writeUInt8(header.protocol_id, offset++); buffer.writeUInt8(header.major, offset++); buffer.writeUInt8(header.minor, offset++); buffer.writeUInt8(header.revision, offset++); return 8; }; //todo: define enumeration for frame types frames.TYPE_AMQP = 0x00; frames.TYPE_SASL = 0x01; frames.read_frame = function(buffer) { var reader = new types.Reader(buffer); var frame = {}; frame.size = reader.read_uint(4); if (reader.remaining() < (frame.size-4)) { return null; } var doff = reader.read_uint(1); if (doff < 2) { throw new errors.ProtocolError('Invalid data offset, must be at least 2 was ' + doff); } frame.type = reader.read_uint(1); if (frame.type === frames.TYPE_AMQP) { frame.channel = reader.read_uint(2); } else if (frame.type === frames.TYPE_SASL) { reader.skip(2); frame.channel = 0; } else { throw new errors.ProtocolError('Unknown frame type ' + frame.type); } if (doff > 1) { //ignore any extended header reader.skip(doff * 4 - 8); } if (reader.remaining()) { frame.performative = reader.read(); var c = by_descriptor[frame.performative.descriptor.value]; if (c) { frame.performative = new c(frame.performative.value); } if (reader.remaining()) { frame.payload = reader.read_bytes(reader.remaining()); } } return frame; }; frames.write_frame = function(frame) { var writer = new types.Writer(); writer.skip(4);//skip size until we know how much we have written writer.write_uint(2, 1);//doff writer.write_uint(frame.type, 1); if (frame.type === frames.TYPE_AMQP) { writer.write_uint(frame.channel, 2); } else if (frame.type === frames.TYPE_SASL) { writer.write_uint(0, 2); } else { throw new errors.ProtocolError('Unknown frame type ' + frame.type); } if (frame.performative) { writer.write(frame.performative); if (frame.payload) { writer.write_bytes(frame.payload); } } var buffer = writer.toBuffer(); buffer.writeUInt32BE(buffer.length, 0);//fill in the size return buffer; }; frames.amqp_frame = function(channel, performative, payload) { return {'channel': channel || 0, 'type': frames.TYPE_AMQP, 'performative': performative, 'payload': payload}; }; frames.sasl_frame = function(performative) { return {'channel': 0, 'type': frames.TYPE_SASL, 'performative': performative}; }; function define_frame(type, def) { var c = types.define_composite(def); frames[def.name] = c.create; by_descriptor[Number(c.descriptor.numeric).toString(10)] = c; by_descriptor[c.descriptor.symbolic] = c; } var open = { name: 'open', code: 0x10, fields: [ {name: 'container_id', type: 'string', mandatory: true}, {name: 'hostname', type: 'string'}, {name: 'max_frame_size', type: 'uint', default_value: 4294967295}, {name: 'channel_max', type: 'ushort', default_value: 65535}, {name: 'idle_time_out', type: 'uint'}, {name: 'outgoing_locales', type: 'symbol', multiple: true}, {name: 'incoming_locales', type: 'symbol', multiple: true}, {name: 'offered_capabilities', type: 'symbol', multiple: true}, {name: 'desired_capabilities', type: 'symbol', multiple: true}, {name: 'properties', type: 'symbolic_map'} ] }; var begin = { name: 'begin', code: 0x11, fields:[ {name: 'remote_channel', type: 'ushort'}, {name: 'next_outgoing_id', type: 'uint', mandatory: true}, {name: 'incoming_window', type: 'uint', mandatory: true}, {name: 'outgoing_window', type: 'uint', mandatory: true}, {name: 'handle_max', type: 'uint', default_value: '4294967295'}, {name: 'offered_capabilities', type: 'symbol', multiple: true}, {name: 'desired_capabilities', type: 'symbol', multiple: true}, {name: 'properties', type: 'symbolic_map'} ] }; var attach = { name: 'attach', code: 0x12, fields:[ {name: 'name', type: 'string', mandatory: true}, {name: 'handle', type: 'uint', mandatory: true}, {name: 'role', type: 'boolean', mandatory: true}, {name: 'snd_settle_mode', type: 'ubyte', default_value: 2}, {name: 'rcv_settle_mode', type: 'ubyte', default_value: 0}, {name: 'source', type: '*'}, {name: 'target', type: '*'}, {name: 'unsettled', type: 'map'}, {name: 'incomplete_unsettled', type: 'boolean', default_value: false}, {name: 'initial_delivery_count', type: 'uint'}, {name: 'max_message_size', type: 'ulong'}, {name: 'offered_capabilities', type: 'symbol', multiple: true}, {name: 'desired_capabilities', type: 'symbol', multiple: true}, {name: 'properties', type: 'symbolic_map'} ] }; var flow = { name: 'flow', code: 0x13, fields:[ {name: 'next_incoming_id', type: 'uint'}, {name: 'incoming_window', type: 'uint', mandatory: true}, {name: 'next_outgoing_id', type: 'uint', mandatory: true}, {name: 'outgoing_window', type: 'uint', mandatory: true}, {name: 'handle', type: 'uint'}, {name: 'delivery_count', type: 'uint'}, {name: 'link_credit', type: 'uint'}, {name: 'available', type: 'uint'}, {name: 'drain', type: 'boolean', default_value: false}, {name: 'echo', type: 'boolean', default_value: false}, {name: 'properties', type: 'symbolic_map'} ] }; var transfer = { name: 'transfer', code: 0x14, fields:[ {name: 'handle', type: 'uint', mandatory: true}, {name: 'delivery_id', type: 'uint'}, {name: 'delivery_tag', type: 'binary'}, {name: 'message_format', type: 'uint'}, {name: 'settled', type: 'boolean'}, {name: 'more', type: 'boolean', default_value: false}, {name: 'rcv_settle_mode', type: 'ubyte'}, {name: 'state', type: 'delivery_state'}, {name: 'resume', type: 'boolean', default_value: false}, {name: 'aborted', type: 'boolean', defaul