http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/lib/rhea.js ---------------------------------------------------------------------- diff --git a/console/stand-alone/plugin/lib/rhea.js b/console/stand-alone/plugin/lib/rhea.js new file mode 100644 index 0000000..fa323a3 --- /dev/null +++ b/console/stand-alone/plugin/lib/rhea.js @@ -0,0 +1,7716 @@ +require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ +(function (process,Buffer){ +/* + * 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 frames = require('./frames.js'); +var log = require('./log.js'); +var sasl = require('./sasl.js'); +var types = require('./types.js'); +var util = require('./util.js'); +var EndpointState = require('./endpoint.js'); +var Session = require('./session.js'); +var Transport = require('./transport.js'); + +var net = require("net"); +var tls = require("tls"); +var EventEmitter = require('events').EventEmitter; + +var AMQP_PROTOCOL_ID = 0x00; +var TLS_PROTOCOL_ID = 0x02; + +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.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 conn_counter = 1; + +var Connection = function (options, container) { + this.options = {}; + if (options) { + for (var k in options) { + this.options[k] = options[k]; + } + } + 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); + if (typeof reconnect === 'boolean' && reconnect) { + var initial = this.get_option('initial_reconnect_delay', 100); + var max = this.get_option('max_reconnect_delay', 60000); + this.options.reconnect = restrict(this.get_option('reconnect_limit'), backoff(initial, max)); + } else if (typeof reconnect === 'number') { + var fixed = this.options.reconnect + this.options.reconnect = restrict(this.get_option('reconnect_limit'), function () { return fixed; }); + } + this.registered = false; + this.state = new EndpointState(); + this.local_channel_map = {}; + this.remote_channel_map = {}; + this.local = {}; + this.remote = {}; + this.local.open = frames.open(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; +}; + +Connection.prototype = Object.create(EventEmitter.prototype); +Connection.prototype.constructor = Connection; +Connection.prototype.dispatch = function(name, context) { + log.events('Connection got event: ' + 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); + } +}; + +Connection.prototype.reset = function() { + if (this.abort_idle) { + this.abort_idle = false; + this.local.close.error = undefined; + this.state = new EndpointState(); + this.state.open(); + } + + //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.state.disconnected(); + this.remote = {}; + //reset sessions: + this.remote_channel_map = {}; + for (var k in this.local_channel_map) { + this.local_channel_map[k].reset(); + } + this.socket_ready = false; +} + +Connection.prototype.connect = function () { + this.is_server = false; + this._connect(this.options.connection_details(this.conn_established_counter)); + this.open(); + return this; +}; +Connection.prototype.reconnect = function () { + log.reconnect('reconnecting...'); + this.reset(); + this._connect(this.options.connection_details(this.conn_established_counter)); + process.nextTick(this._process.bind(this)); + return 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('[' + this.id + '] client accepted: '+ get_socket_id(socket)); + this.socket_ready = true; + return this.init(socket); +}; + +Connection.prototype.init = function (socket) { + this.socket = socket; + this.socket.on('data', this.input.bind(this)); + this.socket.on('error', this.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) { + this.sasl_transport = new sasl.Server(this, mechs); + } + } else { + var mechanisms = this.get_option('sasl_mechanisms'); + if (!mechanisms) { + var username = this.get_option('username'); + var password = this.get_option('password'); + if (username) { + mechanisms = sasl.client_mechanisms(); + if (password) mechanisms.enable_plain(username, password); + 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.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) { + 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.connected = function () { + this.socket_ready = true; + this.conn_established_counter++; + log.io('[' + this.options.id + '] connected ' + get_socket_id(this.socket)); + this.output(); +}; +Connection.prototype.sasl_failed = function (text) { + this.transport_error = {condition:'amqp:unauthorized-access', description:text}; + this._handle_error(); +} + +Connection.prototype._handle_error = function () { + var error = this.get_error(); + if (error) { + //TODO: invoke connection_close regardless of whether connection_error is handled + //TODO: example for error handling + if (!this.dispatch('connection_error', this._context())) { + if (!this.dispatch('connection_close', this._context())) { + console.log('error: ' + JSON.stringify(error)); + } + } + 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 this.remote.close.error; + return undefined; +} + +Connection.prototype.output = function () { + 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); + } + } +}; + +Connection.prototype.input = function (buff) { + if (this.heartbeat_in) clearTimeout(this.heartbeat_in); + log.io('[' + this.options.id + '] read ' + buff.length + ' bytes'); + var buffer; + 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(); +}; + +Connection.prototype.idle = function () { + if (this.is_open()) { + this.abort_idle = true; + this.local.close.error = {condition:'amqp:resource-limit-exceeded', description:'max idle time exceeded'}; + this.close(); + } +}; + +Connection.prototype.error = function (e) { + console.log('[' + this.options.id + '] error: ' + e); + this._disconnected(); +}; + +Connection.prototype.eof = function (e) { + this._disconnected(); +}; + +Connection.prototype._disconnected = function () { + if (!this.is_closed()) { + if (!this.dispatch('disconnected', this._context())) { + console.log('[' + this.options.id + '] disconnected'); + } + if (!this.is_server && !this.transport_error && this.options.reconnect) { + var delay = this.options.reconnect(this.conn_established_counter); + if (delay >= 0) { + log.reconnect('Scheduled reconnect in ' + delay + 'ms'); + setTimeout(this.reconnect.bind(this), delay); + } + } + } +}; + +Connection.prototype.open = function () { + if (this.state.open()) { + this._register(); + } +}; +Connection.prototype.close = function () { + if (this.state.close()) { + this._register(); + } +}; + +Connection.prototype.is_open = function () { + return this.state.is_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.on_open = function (frame) { + if (this.state.remote_opened()) { + this.remote.open = frame.performative; + this.open(); + this.dispatch('connection_open', this._context()); + } else { + throw Error('Open already received'); + } +}; + +Connection.prototype.on_close = function (frame) { + if (this.state.remote_closed()) { + this.remote.close = frame.performative; + this.close(); + if (this.remote.close.error) { + this._handle_error(); + } else { + this.dispatch('connection_close', this._context()); + } + if (this.heartbeat_out) clearTimeout(this.heartbeat_out); + } else { + throw Error('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(); + } + for (var k in this.local_channel_map) { + this.local_channel_map[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.described()); +}; + +Connection.prototype._write_close = function () { + this._write_frame(0, this.local.close.described()); +}; + +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 Error('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._context = function (c) { + var context = c ? c : {}; + context.connection = this; + if (this.container) context.container = this.container; + return context; +}; + +function delegate_to_session(name) { + Connection.prototype['on_' + name] = function (frame) { + var session = this.remote_channel_map[frame.channel]; + if (!session) { + throw Error(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,require('_process'),require("buffer").Buffer) +},{"./endpoint.js":2,"./frames.js":3,"./log.js":5,"./sasl.js":8,"./session.js":9,"./transport.js":11,"./types.js":12,"./util.js":13,"_process":24,"buffer":19,"events":23,"net":18,"tls":18}],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; +}; + +EndpointState.prototype.open = function () { + this.initialised = true; + if (!this.local_open) { + this.local_open = true; + this.open_requests++; + return true; + } else { + return false; + } +}; + +EndpointState.prototype.close = function () { + if (this.local_open) { + this.local_open = false; + this.close_requests++; + return true; + } else { + return false; + } +}; + +EndpointState.prototype.disconnected = function () { + var was_open = this.local_open; + this.init(); + if (was_open) { + this.open(); + } else { + this.close(); + } +}; + +EndpointState.prototype.remote_opened = function (frame) { + if (!this.remote_open) { + this.remote_open = true; + return true; + } else { + return false; + } +}; + +EndpointState.prototype.remote_closed = function (frame) { + 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.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 types = require('./types.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') { + throw Error('Invalid protocol header for AMQP ' + name); + } + header.protocol_id = buffer.readUInt8(offset++); + header.major = buffer.readUInt8(offset++); + header.minor = buffer.readUInt8(offset++); + header.revision = buffer.readUInt8(offset++); + if (header.major !== 1 || header.minor !== 0) { + throw Error('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) { + return null; + } + var doff = reader.read_uint(1); + if (doff < 2) { + throw Error('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); + } else { + throw Error('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 Error('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", default_value:false}, + {name:"batchable", type:"boolean", default_value:false} + ] + }; + +var disposition = {name:"disposition", + code:0x15, + fields:[ + {name:"role", type:"boolean", mandatory:true}, + {name:"first", type:"uint", mandatory:true}, + {name:"last", type:"uint"}, + {name:"settled", type:"boolean", default_value:false}, + {name:"state", type:"*"}, + {name:"batchable", type:"boolean", default_value:false} + ] + }; + +var detach = {name: "detach", + code: 0x16, + fields: [ + {name:"handle", type:"uint", mandatory:true}, + {name:"closed", type:"boolean", default_value:false}, + {name:"error", type:"error"} + ] + }; + +var end = {name: "end", + code: 0x17, + fields: [ + {name:"error", type:"error"} + ] + }; + +var close = {name: "close", + code: 0x18, + fields: [ + {name:"error", type:"error"} + ] + }; + +define_frame(frames.TYPE_AMQP, open); +define_frame(frames.TYPE_AMQP, begin); +define_frame(frames.TYPE_AMQP, attach); +define_frame(frames.TYPE_AMQP, flow); +define_frame(frames.TYPE_AMQP, transfer); +define_frame(frames.TYPE_AMQP, disposition); +define_frame(frames.TYPE_AMQP, detach); +define_frame(frames.TYPE_AMQP, end); +define_frame(frames.TYPE_AMQP, close); + +var sasl_mechanisms = {name:"sasl_mechanisms", code:0x40, + fields: [ + {name:"sasl_server_mechanisms", type:"symbol", multiple:true, mandatory:true} + ]}; + +var sasl_init = {name:"sasl_init", code:0x41, + fields: [ + {name:"mechanism", type:"symbol", mandatory:true}, + {name:"initial_response", type:"binary"}, + {name:"hostname", type:"string"} + ]}; + +var sasl_challenge = {name:"sasl_challenge", code:0x42, + fields: [ + {name:"challenge", type:"binary", mandatory:true} + ]}; + +var sasl_response = {name:"sasl_response", code:0x43, + fields: [ + {name:"response", type:"binary", mandatory:true} + ]}; + +var sasl_outcome = {name:"sasl_outcome", code:0x44, + fields: [ + {name:"code", type:"ubyte", mandatory:true}, + {name:"additional_data", type:"binary"} + ]}; + +define_frame(frames.TYPE_SASL, sasl_mechanisms); +define_frame(frames.TYPE_SASL, sasl_init); +define_frame(frames.TYPE_SASL, sasl_challenge); +define_frame(frames.TYPE_SASL, sasl_response); +define_frame(frames.TYPE_SASL, sasl_outcome); + +module.exports = frames; + +},{"./types.js":12}],4:[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 frames = require('./frames.js'); +var log = require('./log.js'); +var message = require('./message.js'); +var terminus = require('./terminus.js') +var types = require('./types.js') +var EndpointState = require('./endpoint.js'); + +var FlowController = function (window) { + this.window = window; +}; +FlowController.prototype.update = function (context) { + var delta = this.window - context.receiver.credit; + context.receiver.flow(delta); +}; + +function auto_settle(context) { + context.delivery.settled = true; +}; + +function auto_accept(context) { + context.delivery.update(true, message.accepted().described()); +}; + +var EventEmitter = require('events').EventEmitter; + +var link = Object.create(EventEmitter.prototype); +link.dispatch = function(name, context) { + log.events('Link got event: '+ name); + EventEmitter.prototype.emit.apply(this.observers, arguments); + if (this.listeners(name).length) { + EventEmitter.prototype.emit.apply(this, arguments); + } else { + this.session.dispatch.apply(this.session, arguments); + } +}; +link.set_source = function (fields) { + this.local.attach.source = terminus.source(fields).described(); +}; +link.set_target = function (fields) { + this.local.attach.target = terminus.target(fields).described(); +}; + +link.attach = function () { + if (this.state.open()) { + this.connection._register(); + } +}; +link.open = link.attach; + +link.detach = function () { + this.local.detach.closed = false; + if (this.state.close()) { + this.connection._register(); + } +}; +link.close = function() { + this.local.detach.closed = true; + if (this.state.close()) { + this.connection._register(); + } +} + +link.is_open = function () { + return this.session.is_open() && this.state.is_open(); +}; + +link.is_closed = function () { + return this.session.is_closed() || this.state.is_closed(); +}; + +link._process = function () { + do { + if (this.state.need_open()) { + this.session.output(this.local.attach.described()); + } + + if (this.issue_flow) { + this.session._write_flow(this); + this.issue_flow = false; + } + + if (this.state.need_close()) { + this.session.output(this.local.detach.described()); + } + } while (!this.state.has_settled()); +}; + +link.on_attach = function (frame) { + if (this.state.remote_opened()) { + if (!this.remote.handle) { + this.remote.handle = frame.handle; + } + frame.performative.source = terminus.unwrap(frame.performative.source); + frame.performative.target = terminus.unwrap(frame.performative.target); + this.remote.attach = frame.performative; + this.open(); + this.dispatch(this.is_receiver() ? 'receiver_open' : 'sender_open', this._context()); + } else { + throw Error('Attach already received'); + } +}; + +link.on_detach = function (frame) { + if (this.state.remote_closed()) { + this.remote.detach = frame.performative; + this.close(); + this.dispatch(this.local.attach.role ? 'receiver_close' : 'sender_close', this._context()); + } else { + throw Error('Detach already received'); + } +}; + +function is_internal(name) { + switch (name) { + case 'handle': + case 'role': + case 'initial_delivery_count': + return true; + default: + return false; + } +} + +link.init = function (session, name, local_handle, opts, is_receiver) { + this.session = session; + this.connection = session.connection; + this.name = name; + this.options = opts === undefined ? {} : opts; + this.state = new EndpointState(); + this.issue_flow = false;//currently only used by receiver + this.local = {'handle': local_handle}; + this.local.attach = frames.attach({'handle':local_handle,'name':name, role:is_receiver}); + for (var f in this.local.attach) { + if (!is_internal(f) && this.options[f] !== undefined) { + this.local.attach[f] = this.options[f]; + } + } + this.local.detach = frames.detach({'handle':local_handle, 'closed':true}); + this.remote = {'handle':undefined}; + this.delivery_count = 0; + this.credit = 0; + this.observers = new EventEmitter(); +}; +link.reset = function() { + this.state.disconnected(); + this.remote = {'handle':undefined}; + this.delivery_count = 0; + this.credit = 0; +}; + +link.has_credit = function () { + return this.credit > 0; +}; +link.is_receiver = function () { + return this.local.attach.role; +}; +link._context = function (c) { + var context = c ? c : {}; + if (this.is_receiver()) { + context.receiver = this; + } else { + context.sender = this; + } + return this.session._context(context); +}; +link.get_option = function (name, default_value) { + if (this.options[name] !== undefined) return this.options[name]; + else return this.session.get_option(name, default_value); +}; + +var Sender = function (session, name, local_handle, opts) { + this.init(session, name, local_handle, opts, false); + this.local.attach.initial_delivery_count = 0; + this.tag = 0; + if (this.get_option('autosettle', true)) { + this.observers.on('settled', auto_settle); + } +}; +Sender.prototype = Object.create(link); +Sender.prototype.constructor = Sender; +Sender.prototype.next_tag = function () { + return new String(this.tag++); +}; +Sender.prototype.sendable = function (frame) { + return this.credit && this.session.outgoing.available(); +} +Sender.prototype.on_flow = function (frame) { + var flow = frame.performative; + this.credit = flow.delivery_count + flow.link_credit - this.delivery_count; + if (this.is_open()) { + this.dispatch('sender_flow', this._context()); + if (this.sendable()) { + this.dispatch('sendable', this._context()); + } + } +}; +Sender.prototype.on_transfer = function (frame) { + throw Error('got transfer on sending link'); +}; +Sender.prototype.send = function (msg, tag) { + return this.session.send(this, tag ? tag : this.next_tag(), message.encode(msg), 0); +}; + + +var Receiver = function (session, name, local_handle, opts) { + this.init(session, name, local_handle, opts, true); + this.set_prefetch(this.get_option('prefetch', 100)); + if (this.get_option('autoaccept', true)) { + this.observers.on('message', auto_accept); + } +}; +Receiver.prototype = Object.create(link); +Receiver.prototype.constructor = Receiver; +Receiver.prototype.on_flow = function (frame) { + this.dispatch('receiver_flow', this._context()); +}; +Receiver.prototype.flow = function(credit) { + if (credit > 0) { + this.credit += credit; + this.issue_flow = true; + this.connection._register(); + } +}; + +Receiver.prototype.set_prefetch = function(prefetch) { + if (prefetch > 0) { + var flow_controller = new FlowController(prefetch); + var listener = flow_controller.update.bind(flow_controller); + this.observers.on('message', listener); + this.observers.on('receiver_open', listener); + } +} + +module.exports = {'Sender': Sender, 'Receiver':Receiver}; + +},{"./endpoint.js":2,"./frames.js":3,"./log.js":5,"./message.js":6,"./terminus.js":10,"./types.js":12,"events":23}],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 debug = require('debug'); + +module.exports = { + 'frames' : debug('rhea:frames'), + 'raw' : debug('rhea:raw'), + 'reconnect' : debug('rhea:reconnect'), + 'events' : debug('rhea:events'), + 'message' : debug('rhea:message'), + 'flow' : debug('rhea:flow'), + 'io' : debug('rhea:io') +} + +},{"debug":15}],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 log = require('./log.js'); +var types = require('./types.js'); + +var by_descriptor = {}; +var unwrappers = {}; +var wrappers = []; +var message = {}; + +function define_section(descriptor, unwrap, wrap) { + unwrap.descriptor = descriptor; + unwrappers[descriptor.symbolic] = unwrap; + unwrappers[Number(descriptor.numeric).toString(10)] = unwrap; + if (wrap) { + wrappers.push(wrap); + } +}; + +function define_composite_section(def) { + var c = types.define_composite(def); + message[def.name] = c.create; + by_descriptor[Number(c.descriptor.numeric).toString(10)] = c; + by_descriptor[c.descriptor.symbolic] = c; + + var unwrap = function (msg, section) { + msg[def.name] = new c(section.value); + }; + + var wrap = function (sections, msg) { + if (msg[def.name]) { + if (msg[def.name].described) { + sections.push(msg[def.name].described()); + } else { + sections.push(c.create(msg[def.name]).described()); + } + } + }; + define_section(c.descriptor, unwrap, wrap); +}; + + +function define_map_section(def) { + var descriptor = {numeric:def.code}; + descriptor.symbolic = 'amqp:' + def.name.replace(/_/g, '-') + ':map'; + var unwrap = function (msg, section) { + msg[def.name] = types.unwrap(section); + }; + var wrap = function (sections, msg) { + if (msg[def.name]) { + sections.push(types.described(types.wrap_ulong(descriptor.numeric), types.wrap_map(msg[def.name]))); + } + }; + define_section(descriptor, unwrap, wrap); +}; + +define_composite_section({name:"header", + code:0x70, + fields:[ + {name:"durable", type:"boolean", default_value:false}, + {name:"priority", type:"ubyte", default_value:4}, + {name:"ttl", type:"uint"}, + {name:"first_acquirer", type:"boolean", default_value:false}, + {name:"delivery_count", type:"uint", default_value:0} + ] + }); +define_map_section({name:"delivery_annotations", code:0x71}); +define_map_section({name:"message_annotations", code:0x72}); +define_composite_section({name:"properties", + code:0x73, + fields:[ + {name:"message_id", type:"message_id"}, + {name:"user_id", type:"binary"}, + {name:"to", type:"string"}, + {name:"subject", type:"string"}, + {name:"reply_to", type:"string"}, + {name:"correlation_id", type:"message_id"}, + {name:"content_type", type:"symbol"}, + {name:"content_encoding", type:"symbol"}, + {name:"absolute_expiry_time", type:"timestamp"}, + {name:"creation_time", type:"timestamp"}, + {name:"group_id", type:"string"}, + {name:"group_sequence", type:"uint"}, + {name:"reply_to_group_id", type:"string"} + ] + }); +define_map_section({name:"application_properties", code:0x74}); + +define_section({numeric:0x77, symbolic:'amqp:value:*'}, + function(msg, section) { msg.body = types.unwrap(section); }, + function(sections, msg) { sections.push(types.described(types.wrap_ulong(0x77), types.wrap(msg.body))); }); + +define_map_section({name:"footer", code:0x78}); + +message.encode = function(obj) { + var sections = []; + + wrappers.forEach(function (wrapper_fn) { wrapper_fn(sections, obj); }); + var writer = new types.Writer(); + for (var i = 0; i < sections.length; i++) { + log.message('Encoding section ' + (i+1) + ' of ' + sections.length + ': ' + sections[i]); + writer.write(sections[i]); + } + var data = writer.toBuffer(); + log.message('encoded ' + data.length + ' bytes'); + return data; +} + +message.decode = function(buffer) { + var msg = {}; + var reader = new types.Reader(buffer); + while (reader.remaining()) { + var s = reader.read(); + log.message('decoding section: ' + JSON.stringify(s) + ' of type: ' + JSON.stringify(s.descriptor)); + if (s.descriptor) { + var unwrap = unwrappers[s.descriptor.value]; + if (unwrap) { + unwrap(msg, s); + } else { + console.log("WARNING: did not recognise message section with descriptor " + s.descriptor); + } + } else { + console.log("WARNING: expected described message section got " + JSON.stringify(s)); + } + } + return msg; +} + +var outcomes = {}; + +function define_outcome(def) { + var c = types.define_composite(def); + c.composite_type = def.name; + message[def.name] = c.create; + outcomes[Number(c.descriptor.numeric).toString(10)] = c; + outcomes[c.descriptor.symbolic] = c; + message['is_' + def.name] = function (o) { + if (o && o.descriptor) { + var c = outcomes[o.descriptor.value]; + if (c) { + return c.descriptor.numeric == def.code; + } + } + return false; + }; +} + +message.unwrap_outcome = function (outcome) { + if (outcome && outcome.descriptor) { + var c = outcomes[outcome.descriptor.value]; + if (c) { + return new c(outcome); + } + } + console.log('unrecognised outcome'); + return outcome; +}; + +message.are_outcomes_equivalent = function(a, b) { + if (a === undefined && b === undefined) return true; + else if (a === undefined || b === undefined) return false; + else return a.descriptor.value == b.descriptor.value && JSON.stringify(a) == JSON.stringify(b); +}; + +define_outcome({name:"received", code:0x23, + fields:[ + {name:"section-number", type:"uint", mandatory:true}, + {name:"section-offset", type:"ulong", mandatory:true} + ]}); +define_outcome({name:"accepted", code:0x24, fields:[]}); +define_outcome({name:"rejected", code:0x25, fields:[{name:"error", type:"error"}]}); +define_outcome({name:"released", code:0x26, fields:[]}); +define_outcome({name:"modified", + code:0x27, + fields:[ + {name:"delivery-failed", type:"boolean"}, + {name:"undeliverable-here", type:"boolean"}, + {name:"message-annotations", type:"fields"} +]}); + +module.exports = message; + +},{"./log.js":5,"./types.js":12}],7:[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 url = require('url'); + +var simple_id_generator = { + counter : 1, + next : function() { + return this.counter++; + } +}; + +var Client = function (container, address) { + var u = url.parse(address); + //TODO: handle scheme and user/password if present + this.connection = container.connect({'host':u.hostname, 'port':u.port}); + this.connection.on('message', this._response.bind(this)); + this.connection.on('receiver_open', this._ready.bind(this)); + this.sender = this.connection.attach_sender(u.path.substr(1)); + this.receiver = this.connection.attach_receiver({source:{dynamic:true}}); + this.id_generator = simple_id_generator; + this.pending = [];//requests yet to be made (waiting for receiver to open) + this.outstanding = {};//requests sent, for which responses have not yet been received +}; + +Client.prototype._request = function (id, name, args, callback) { + var request = {properties:{}}; + request.properties.subject = name; + request.body = args; + request.properties.message_id = id; + request.properties.reply_to = this.receiver.remote.attach.source.address; + this.outstanding[id] = callback; + this.sender.send(request); +}; + +Client.prototype._response = function (context) { + var id = context.message.properties.correlation_id; + var callback = this.outstanding[id]; + if (callback) { + if (context.message.properties.subject === 'ok') { + callback(context.message.body); + } else { + callback(undefined, {name: context.message.properties.subject, description: context.message.body}); + } + } else { + console.log('no request pending for ' + id + ', ignoring response'); + } +}; + +Client.prototype._ready = function (context) { + this._process_pending(); +}; + +Client.prototype._process_pending = function () { + for (var i = 0; i < this.pending.length; i++) { + var r = this.pending[i]; + this._request(r.id, r.name, r.args, r.callback); + } + this.pending = []; +}; + +Client.prototype.call = function (name, args, callback) { + var id = this.id_generator.next(); + if (this.receiver.is_open() && this.pending.length === 0) { + this._request(id, name, args, callback); + } else { + //need to wait for reply-to address + this.pending.push({'name':name, 'args':args, 'callback':callback, 'id':id}); + } +}; + +Client.prototype.close = function () { + this.receiver.close(); + this.sender.close(); + this.connection.close(); +}; + +Client.prototype.define = function (name) { + this[name] = function (args, callback) { this.call(name, args, callback); }; +}; + +var Cache = function (ttl, purged) { + this.ttl = ttl; + this.purged = purged; + this.entries = {}; + this.timeout = undefined; +}; + +Cache.prototype.clear = function () { + if (this.timeout) clearTimeout(this.timeout); + this.entries = {}; +} + +Cache.prototype.put = function (key, value) { + this.entries[key] = {'value':value, 'last_accessed': Date.now()}; + if (!this.timeout) this.timeout = setTimeout(this.purge.bind(this), this.ttl); +}; + +Cache.prototype.get = function (key) { + var entry = this.entries[key]; + if (entry) { + entry.last_accessed = Date.now(); + return entry.value; + } else { + return undefined; + } +}; + +Cache.prototype.purge = function() { + //TODO: this could be optimised if the map is large + var now = Date.now(); + var expired = []; + var live = 0; + for (var k in this.entries) { + if (now - this.entries[k].last_accessed >= this.ttl) { + expired.push(k); + } else { + live++; + } + } + for (var i = 0; i < expired.length; i++) { + var entry = this.entries[expired[i]]; + delete this.entries[expired[i]]; + this.purged(entry.value); + } + if (live && !this.timeout) { + this.timeout = setTimeout(this.purge.bind(this), this.ttl); + } +}; + +var LinkCache = function (factory, ttl) { + this.factory = factory; + this.cache = new Cache(ttl, function(link) { link.close(); }); +} + +LinkCache.prototype.clear = function () { + this.cache.clear(); +} + +LinkCache.prototype.get = function (address) { + var link = this.cache.get(address); + if (link === undefined) { + link = this.factory(address); + this.cache.put(address, link); + } + return link; +}; + +var Server = function (container, address, options) { + this.options = options || {}; + var u = url.parse(address); + //TODO: handle scheme and user/password if present + this.connection = container.connect({'host':u.hostname, 'port':u.port}); + this.connection.on('connection_open', this._connection_open.bind(this)); + this.connection.on('message', this._request.bind(this)); + this.receiver = this.connection.attach_receiver(u.path.substr(1)); + this.callbacks = {}; + this._send = undefined; + this._clear = undefined; +}; + +function match(desired, offered) { + if (offered) { + if (Array.isArray(offered)) { + return offered.indexOf(desired) > -1; + } else { + return desired === offered; + } + } else { + return false; + } +} + +Server.prototype._connection_open = function (context) { + if (match('ANONYMOUS-RELAY', this.connection.remote.open.offered_capabilities)) { + var relay = this.connection.attach_sender({target:{}}); + this._send = function (msg) { relay.send(msg); }; + } else { + var cache = new LinkCache(this.connection.attach_sender.bind(this.connection), this.options.cache_ttl || 60000); + this._send = function (msg) { var s = cache.get(msg.properties.to); if (s) s.send(msg); }; + this._clear = function () { cache.clear(); } + } +} + +Server.prototype._respond = function (response) { + var server = this; + return function (result, error) { + if (error) { + response.properties.subject = error.name || 'error'; + response.body = error.description || error; + } else { + response.properties.subject = 'ok'; + response.body = result; + } + server._send(response); + }; +} + +Server.prototype._request = function (context) { + var request = context.message; + var response = {properties:{}}; + response.properties.to = request.properties.reply_to; + response.properties.correlation_id = request.properties.message_id; + var callback = this.callbacks[request.properties.subject]; + if (callback) { + callback(request.body, this._respond(response)); + } else { + response.properties.subject = 'bad-method'; + response.body = 'Unrecognised method ' + request.properties.subject; + this._send(response); + } +}; + +Server.prototype.bind_sync = function (f, name) { + this.callbacks[name || f.name] = function (args, callback) { var result = f(args); callback(result); }; +}; +Server.prototype.bind = function (f, name) { + this.callbacks[name || f.name] = f; +}; + +Server.prototype.close = function () { + if (this._clear) this._clear(); + this.receiver.close(); + this.connection.close(); +}; + +module.exports = { + server : function(container, address, options) { return new Server(container, address, options); }, + client : function(connection, address) { return new Client(connection, address); } +}; + +},{"url":29}],8:[function(require,module,exports){ +(function (Buffer){ +/* + * 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 frames = require('./frames.js'); +var log = require('./log.js'); +var Transport = require('./transport.js'); + +var sasl_codes = { + "OK":0, + "AUTH":1, + "SYS":2, + "SYS_PERM":3, + "SYS_TEMP":4, +}; + +var SASL_PROTOCOL_ID = 0x03; + +function intersection(lista, listb) { + return lista.filter(function (a) { return listb.indexOf(a) >= 0; }); +} +function extract(buffer) { + var results = []; + var start = 0; + var i = 0; + while (i < buffer.length) { + if (buffer[i] === 0x00) { + if (i > start) results.push(buffer.toString('utf8', start, i)); + else results.push(null); + start = ++i; + } else { + ++i; + } + } + if (i > start) results.push(buffer.toString('utf8', start, i)); + else results.push(null); + return results; +} + +var PlainServer = function(callback) { + this.callback = callback; + this.outcome = undefined; + this.username = undefined; +}; + +PlainServer.prototype.start = function(response) { + var fields = extract(response); + if (fields.length !== 3) { + this.connection.sasl_failed('Unexpected response in PLAIN, got ' + fields.length + ' fields, expected 3'); + } + if (this.callback(fields[1], fields[2])) { + this.outcome = true; + this.username = fields[1]; + } else { + this.outcome = false; + } +}; + +var PlainClient = function(username, password) { + this.username = username; + this.password = password; +}; + +PlainClient.prototype.start = function() { + var response = new Buffer(1 + this.username.length + 1 + this.password.length); + response.writeUInt8(0, 0); + response.write(this.username, 1); + response.writeUInt8(0, 1 + this.username.length); + response.write(this.password, 1 + this.username.length + 1); + return response; +}; + +var AnonymousServer = function() { + this.outcome = undefined; + this.username = undefined; +}; + +AnonymousServer.prototype.start = function(response) { + this.outcome = true; + this.username = response ? response.toString('utf8') : 'anonymous'; +}; + +var AnonymousClient = function(name) { + this.username = name ? name : 'anonymous'; +}; + +AnonymousClient.prototype.start = function() { + var response = new Buffer(1 + this.username.length); + response.writeUInt8(0, 0); + response.write(this.username, 1); + return response; +}; + +var ExternalServer = function() { + this.outcome = undefined; + this.username = undefined; +}; + +ExternalServer.prototype.start = function(response) { + this.outcome = true; +}; + +var ExternalClient = function() { + this.username = undefined; +}; + +ExternalClient.prototype.start = function() { + return null; +}; + +/** + * The mechanisms argument is a map of mechanism names to factory + * functions for objects that implement that mechanism. + */ +var SaslServer = function (connection, mechanisms) { + this.connection = connection; + this.transport = new Transport(connection.amqp_transport.identifier, SASL_PROTOCOL_ID, frames.TYPE_SASL, this); + this.next = connection.amqp_transport; + this.mechanisms = mechanisms; + this.mechanism = undefined; + this.outcome = undefined; + this.username = undefined; + var mechlist = Object.getOwnPropertyNames(mechanisms); + this.transport.encode(frames.sasl_frame(frames.sasl_mechanisms({sasl_server_mechanisms:mechlist}).described())); +}; + +SaslServer.prototype.do_step = function (challenge) { + if (this.mechanism.outcome === undefined) { + this.transport.encode(frames.sasl_frame(frames.sasl_challenge({'challenge':challenge}).described())); + } else { + this.outcome = this.mechanism.outcome ? sasl_codes.OK : sasl_codes.AUTH; + this.transport.encode(frames.sasl_frame(frames.sasl_outcome({code: this.outcome}).described())); + if (this.outcome === sasl_codes.OK) { + this.username = this.mechanism.username; + this.transport.write_complete = true; + this.transport.read_complete = true; + } + } +}; + +SaslServer.prototype.on_sasl_init = function (frame) { + var f = this.mechanisms[frame.performative.mechanism]; + if (f) { + this.mechanism = f(); + var challenge = this.mechanism.start(frame.performative.initial_response); + this.do_step(challenge); + } else { + this.outcome = sasl_codes.AUTH; + this.transport.encode(frames.sasl_frame(frames.sasl_outcome({code: this.outcome}).described())); + } +}; +SaslServer.prototype.on_sasl_response = function (frame) { + this.do_step(this.mechanism.step(frame.performative.response)); +}; + +SaslServer.prototype.has_writes_pending = function () { + return this.transport.has_writes_pending() || this.next.has_writes_pending(); +} + +SaslServer.prototype.write = function (socket) { + if (this.transport.write_complete && this.transport.pending.length === 0) { + return this.next.write(socket); + } else { + return this.transport.write(socket); + } +}; + +SaslServer.prototype.read = function (buffer) { + if (this.transport.read_complete) { + return this.next.read(buffer); + } else { + return this.transport.read(buffer); + } +}; + +var SaslClient = function (connection, mechanisms) { + this.connection = connection; + this.transport = new Transport(connection.amqp_transport.identifier, SASL_PROTOCOL_ID, frames.TYPE_SASL, this); + this.next = connection.amqp_transport; + this.mechanisms = mechanisms; + this.mechanism = undefined; + this.mechanism_name = undefined; + this.failed = false; +}; + +SaslClient.prototype.on_sasl_mechanisms = function (frame) { + for (var i = 0; this.mechanism === undefined && i < frame.performative.sasl_server_mechanisms.length; i++) { + var mech = frame.performative.sasl_server_mechanisms[i]; + var f = this.mechanisms[mech]; + if (f) { + this.mechanism = f(); + this.mechanism_name = mech; + } + } + if (this.mechanism) { + var response = this.mechanism.start(); + this.transport.encode(frames.sasl_frame(frames.sasl_init({'mechanism':this.mechanism_name,'initial_response':response}).described())); + } else { + this.failed = true; + this.connection.sasl_failed('No suitable mechanism; server supports ' + frame.performative.sasl_server_mechanisms); + } +}; +SaslClient.prototype.on_sasl_challenge = function (frame) { + var response = this.mechanism.step(frame.performative.challenge); + this.transport.encode(frames.sasl_frame(frames.sasl_response({'response':response}).described())); +}; +SaslClient.prototype.on_sasl_outcome = function (frame) { + switch (frame.performative.code) { + case sasl_codes.OK: + this.transport.read_complete = true; + this.transport.write_complete = true; + break; + default: + this.transport.write_complete = true; + this.connection.sasl_failed("Failed to authenticate: " + frame.performative.code); + } +}; + +SaslClient.prototype.has_writes_pending = function () { + return this.transport.has_writes_pending() || this.next.has_writes_pending(); +} + +SaslClient.prototype.write = function (socket) { + if (this.transport.write_complete) { + return this.next.write(socket); + } else { + return this.transport.write(socket); + } +}; + +SaslClient.prototype.read = function (buffer) { + if (this.transport.read_complete) { + return this.next.read(buffer); + } else { + return this.transport.read(buffer); + } +}; + +var default_server_mechanisms = { + enable_anonymous: function () { + this['ANONYMOUS'] = function() { return new AnonymousServer(); }; + }, + enable_plain: function (callback) { + this['PLAIN'] = function() { return new PlainServer(callback); }; + } +}; + +var default_client_mechanisms = { + enable_anonymous: function (name) { + this['ANONYMOUS'] = function() { return new AnonymousClient(name); }; + }, + enable_plain: function (username, password) { + this['PLAIN'] = function() { return new PlainClient(username, password); }; + }, + enable_external: function () { + this['EXTERNAL'] = function() { return new ExternalClient(); }; + } +}; + +module.exports = { + Client : SaslClient, + Server : SaslServer, + server_mechanisms : function () { + return Object.create(default_server_mechanisms); + }, + client_mechanisms : function () { + return Object.create(default_client_mechanisms); + }, + server_add_external: function (mechs) { + mechs['EXTERNAL'] = function() { return new ExternalServer(); }; + return mechs; + } +}; + +}).call(this,require("buffer").Buffer) +},{"./frames.js":3,"./log.js":5,"./transport.js":11,"buffer":19}],9:[function(require,module,exports){ +(function (Buffer){ +/* + * 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 frames = require('./frames.js'); +var link = require('./link.js'); +var log = require('./log.js'); +var message = require('./message.js'); +var types = require('./types.js'); +var util = require('./util.js'); +var EndpointState = require('./endpoint.js'); + +var EventEmitter = require('events').EventEmitter; + +var CircularBuffer = function (capacity) { + this.capacity = capacity; + this.size = 0; + this.head = 0; + this.tail = 0; + this.entries = []; +}; + +CircularBuffer.prototype.available = function () { + return this.capacity - this.size; +}; + +CircularBuffer.prototype.push = function (o) { + if (this.size < this.capacity) { + this.entries[this.tail] = o; + this.tail = (this.tail + 1) % this.capacity; + this.size++; + } else { + throw Error('circular buffer overflow: head=' + this.head + ' tail=' + this.tail + ' size=' + this.size + ' capacity=' + this.capacity); + } +}; + +CircularBuffer.prototype.pop_if = function (f) { + var count = 0; + while (this.size && f(this.entries[this.head])) { + this.entries[this.head] = undefined; + this.head = (this.head + 1) % this.capacity; + this.size--; + count++; + } + return count; +}; + +CircularBuffer.prototype.by_id = function (id) { + if (this.size > 0) { + var gap = id - this.entries[this.head].id; + if (gap < this.size) { + return this.entries[(this.head + gap) % this.capacity]; + } + } + return undefined; +}; + +CircularBuffer.prototype.get_head = function (id) { + return this.size > 0 ? this.entries[this.head] : undefined; +}; + + +var Outgoing = function () { + this.deliveries = new CircularBuffer(2048/*TODO= configurable?*/); + this.updated = []; + this.next_delivery_id = 0; + this.next_pending_delivery = 0; + this.next_transfer_id = 0; + this.window = types.MAX_UINT; + this.remote_next_transfer_id = undefined; + this.remote_window = undefined; +}; + +Outgoing.prototype.available = function () { + return this.deliveries.available(); +}; + +Outgoing.prototype.send = function (sender, tag, data, format) { + var d = {'id':this.next_delivery_id++, + 'tag':tag, + 'link':sender, + 'data': data, + 'format':format ? format : 0, + 'sent': false, + 'settled': false, + 'state': undefined, + 'remote_settled': false, + 'remote_state': undefined}; + this.deliveries.push(d); + return d; +}; + +Outgoing.prototype.on_begin = function (fields) { + this.remote_window = fields.incoming_window; +}; + +Outgoing.prototype.on_flow = function (fields) { + this.remote_next_transfer_id = fields.next_incoming_id; + this.remote_window = fields.incoming_window; +}; + +Outgoing.prototype.on_disposition = function (fields) { + var last = fields.last ? fields.last : fields.first; + for (var i = fields.first; i <= last; i++) { + var d = this.deliveries.by_id(i); + if (!d) { + console.log('Could not find delivery for ' + i + ' [' + JSON.stringify(fields) + ']'); + } + if (d && !d.remote_settled) { + var updated = false; + if (fields.settled) { + d.remote_settled = fields.settled; + updated = true; + } + if (fields.state && fields.state !== d.remote_state) { + d.remote_state = fields.state; + updated = true; + } + if (updated) { + this.updated.push(d); + } + } + } +}; + +Outgoing.prototype.transfer_window = function() { + if (this.remote_window) { + return this.remote_window - (this.next_transfer_id - this.remote_next_transfer_id); + } +}; + +Outgoing.prototype.process = function() { + // send pending deliveries for which there is credit: + while (this.next_pending_delivery < this.next_delivery_id) { + var d = this.deliveries.by_id(this.next_pending_delivery); + if (d) { + if (d.link.has_credit()) { + d.link.delivery_count++; + //TODO: fragment as appropriate + d.transfers_required = 1; + if (this.transfer_window() >= d.transfers_required) { + this.next_transfer_id += d.transfers_required; + this.window -= d.transfers_required; + d.link.session.output(frames.transfer({'handle':d.link.local.handle,'message_format':d.format,'delivery_id':d.id, 'delivery_tag':d.tag}).described(), d.data); + d.link.credit--; + this.next_pending_delivery++; + } else { + log.flow('Incoming window of peer preventing sending further transfers: remote_window=' + this.remote_window + ", remote_next_transfer_id=" + this.remote_next_transfer_id + + ", next_transfer_id=" + this.next_transfer_id); + break; + } + } else { + log.flow('Link has no credit'); + break; + } + } else { + console.log('ERROR: Next pending delivery not found: ' + this.next_pending_delivery); + break; + } + } + + // notify application of any updated deliveries: + for (var i = 0; i < this.updated.length; i++) { + var d = this.updated[i]; + if (d.remote_state) { + d.remote_state = message.unwrap_outcome(d.remote_state); + if (d.remote_state && d.remote_state.constructor.composite_type) { + d.link.dispatch(d.remote_state.constructor.composite_type, d.link._context({'delivery':d})); + } + } + if (d.remote_settled) d.link.dispatch('settled', d.link._context({'delivery':d})); + } + this.updated = []; + + // remove any fully settled deliveries: + this.deliveries.pop_if(function (d) { return d.settled && d.remote_settled; }); +}; + +var Incoming = function () { + this.deliveries = new CircularBuffer(2048/*TODO: configurable?*/); + this.updated = []; + this.next_transfer_id = 0; + this.next_delivery_id = undefined; + this.window = 2048/*TODO: configurable?*/; + this.remote_next_transfer_id = undefined; + this.remote_window = undefined; +}; + +Incoming.prototype.update = function (delivery, settled, state) { + if (delivery) { + delivery.settled = settled; + if (state !== undefined) delivery.state = state; + if (!delivery.remote_settled) { + this.updated.push(delivery); + } + delivery.link.connection._register(); + } +}; + +Incoming.prototype.on_transfer = function(frame, receiver) { + this.next_transfer_id++; + if (receiver.is_open()) { + if (this.next_delivery_id === undefined) { + this.next_delivery_id = frame.performative.delivery_id; + } + var current; + var data; + var last = this.deliveries.get_head(); + if (last && last.incomplete) { + if (frame.performative.delivery_id !== undefined && this.next_delivery_id != frame.performative.delivery_id) { + //TODO: better error handling + throw Error("frame sequence error: delivery " + this.next_delivery_id + " not complete, got " + frame.performative.delivery_id); + } + current = last; + data = Buffer.concat([current.data, frame.payload], current.data.size() + frame.payload.size()); + } else if (this.next_delivery_id === frame.performative.delivery_id) { + current = {'id':frame.performative.delivery_id, + 'tag':frame.performative.delivery_tag, + 'link':receiver, + 'settled': false, + 'state': undefined, + 'remote_settled': frame.performative.settled, + 'remote_state': undefined}; + var self = this; + current.update = function (settled, state) { self.update(current, settled, state); }; + this.deliveries.push(current); + data = frame.payload; + } else { + //TODO: better error handling + throw Error("frame sequence error: expected " + this.next_delivery_id + ", got " + frame.performative.delivery_id); + } + current.incomplete = frame.performative.more; + if (current.incomplete) { + current.data = data; + } else { + receiver.credit--; + receiver.delivery_count++; + this.next_delivery_id++; + receiver.dispatch('message', receiver._context({'message':message.decode(data), 'delivery':current})); + } + } +}; + +Incoming.prototype.process = function () { + if (this.updated.length > 0) { + var first; + var last; + var next_id; + + for (var i = 0; i < this.updated.length; i++) { + var delivery = this.updated[i]; + if (first === undefined) { + first = delivery; + last = delivery; + next_id = delivery.id; + } + + if (!message.are_outcomes_equivalent(last.state, delivery.state) || last.settled !== delivery.settled || next_id !== delivery.id) { + first.link.session.output(frames.disposition({'role':true,'first':first.id,'last':last.id, 'state':first.state, 'settled':first.settled}).described()); + first = delivery; + last = delivery; + next_id = delivery.id; + } else { + if (last.id !== delivery.id) { + last = delivery; + } + next_id++; + } + } + if (first !== undefined && last !== undefined) { + first.link.session.output(frames.disposition({'role':true,'first':first.id,'last':last.id, 'state':first.state, 'settled':first.settled}).described()); + } + + this.updated = []; + } + + // remove any fully settled deliveries: + this.deliveries.pop_if(function (d) { return d.settled; }); +}; + +Incoming.prototype.on_begin = function (fields) { + this.remote_window = fields.outgoing_window; +}; + +Incoming.prototype.on_flow = function (fields) { + this.remote_next_transfer_id = fields.next_outgoing_id; + this.remote_window = fields.outgoing_window; +}; + +Incoming.prototype.on_disposition = function (fields) { + var last = fields.last ? fields.last : fields.first; + for (var i = fields.first; i <= last; i++) { + var d = this.deliveries.by_id(i); + if (!d) { + console.log('Could not find delivery for ' + i); + } + if (d && !d.remote_settled) { + var updated = false; + if (fields.settled) { + d.remote_settled = fields.settled; + updated = true; + } + if (fields.state && fields.state !== d.remote_state) { + d.remote_state = fields.state; + updated = true; + } + if (updated) { + console.log(d.link.connection.options.id + ' added delivery to updated list following receipt of disposition for incoming deliveries'); + this.updated.push(d); + } + } + } + +}; + +var Session = function (connection, local_channel) { + this.connection = connection; + this.outgoing = new Outgoing(); + this.incoming = new Incoming(); + this.state = new EndpointState(); + this.local = {'channel': local_channel, 'handles':{}}; + this.local.begin = frames.begin({next_outgoing_id:this.outgoing.next_transfer_id,incoming_window:this.incoming.window,outgoing_window:this.outgoing.window}); + this.local.end = frames.end(); + this.remote = {'handles':{}}; + this.links = {}; // map by name + this.options = {}; +}; +Session.prototype = Object.create(EventEmitter.prototype); +Session.prototype.constructor = Session; + +Session.prototype.reset = function() { + this.state.disconnected(); + this.outgoing = new Outgoing(); + this.incoming = new Incoming(); + this.remote = {'handles':{}}; + for (var l in this.links) { + this.links[l].reset(); + } +}; + +Session.prototype.dispatch = function(name, context) { + log.events('Session got event: '+ name); + if (this.listeners(name).length) { + EventEmitter.prototype.emit.apply(this, arguments); + } else { + this.connection.dispatch.apply(this.connection, arguments); + } +}; +Session.prototype.output = function (frame, payload) { + this.connection._write_frame(this.local.channel, frame, payload); +}; + +Session.prototype.create_sender = function (name, opts) { + return this.create_link(name, link.Sender, opts); +}; + +Session.prototype.create_receiver = function (name, opts) { + return this.create_link(name, link.Receiver, opts); +}; + +function attach(factory, args, remote_terminus) { + var opts = args ? args : {}; + if (typeof args === 'string') { + opts = {}; + opts[remote_terminus] = args; + } + if (!opts.name) opts.name = util.generate_uuid(); + var l = factory(opts.name, opts); + for (var t in {'source':0, 'target':0}) { + if (opts[t]) { + if (typeof opts[t] === 'string') { + opts[t] = {'address' : opts[t]}; + } + l['set_' + t](opts[t]); + } + } + l.attach(); + return l; +} + +Session.prototype.get_option = function (name, default_value) { + if (this.options[name] !== undefined) return this.options[name]; + else return this.connection.get_option(name, default_value); +}; + +Session.prototype.attach_sender = function (args) { + return attach(this.create_sender.bind(this), args, 'target'); +}; +Session.prototype.open_sender = Session.prototype.attach_sender;//alias + +Session.prototype.attach_receiver = function (args) { + return attach(this.create_receiver.bind(this), args, 'source'); +}; +Session.prototype.open_receiver = Session.prototype.attach_receiver;//alias + +Session.prototype.create_link = function (name, constructor, opts) { + var i = 0; + while (this.local.handles[i]) i++; + var l = new constructor(this, name, i, opts); + this.links[name] = l; + this.local.handles[i] = l; + return l; +}; + +Session.prototype.begin = function () { + if (this.state.open()) { + this.connection._register(); + } +}; +Session.prototype.open = Session.prototype.begin; + +Session.prototype.end = function () { + if (this.state.close()) { + this.connection._register(); + } +}; +Session.prototype.close = Session.prototype.end; + +Session.prototype.is_open = function () { + return this.connection.is_open() && this.state.is_open(); +}; + +Session.prototype.is_closed = function () { + return this.connection.is_closed() || this.state.is_closed(); +}; + +Session.prototype._process = function () { + do { + if (this.state.need_open()) { + this.output(this.local.begin.described()); + } + + this.outgoing.process(); + this.incoming.process(); + for (var k in this.links) { + this.links[k]._process(); + } + + if (this.state.need_close()) { + this.output(this.local.end.described()); + } + } while (!this.state.has_settled()); +}; + +Session.prototype.send = function (sender, tag, data, format) { + var d = this.outgoing.send(sender, tag, data, format); + this.connection._register(); + return d; +}; + +Session.prototype._write_flow = function (link) { + var fields = {'next_incoming_id':this.incoming.next_transfer_id, + 'incoming_window':this.incoming.window, + 'next_outgoing_id':this.outgoing.next_transfer_id, + 'outgoing_window':this.outgoing.window + }; + if (link) { + fields.delivery_count = link.delivery_count; + fields.handle = link.local.handle; + fields.link_credit = link.credit; + } + this.output(frames.flow(fields).described()); +}; + +Session.prototype.on_begin = function (frame) { + if (this.state.remote_opened()) { + if (!this.remote.channel) { + this.remote.channel = frame.channel; + } + this.remote.begin = frame.performative; + this.outgoing.on_begin(frame.performative); + this.incoming.on_begin(frame.performative); + this.open(); + this.dispatch('session_open', this._context()); + } else { + throw Error('Begin already received'); + } +}; +Session.prototype.on_end = function (frame) { + if (this.state.remote_closed()) { + this.remote.end = frame.performative; + this.close(); + this.dispatch('session_close', this._context()); + } else { + throw Error('End already received'); + } +}; + +Session.prototype.on_attach = function (frame) { + var name = frame.performative.name; + var link = this.links[name]; + if (!link) { + // if role is true, peer is receiver, so we are sender + link = frame.performative.role ? this.create_sender(name) : this.create_receiver(name); + } + this.remote.handles[frame.performative.handle] = link; + link.on_attach(frame); + link.remote.attach = frame.performative; +}; + +Session.prototype.on_disposition = function (frame) { + if (frame.performative.role) { + log.events('Received disposition for outgoing transfers'); + this.outgoing.on_disposition(frame.performative); + } else { + log.events('Received disposition for incoming transfers'); + this.incoming.on_disposition(frame.performative); + } + this.connection._register(); +} + +Session.prototype.on_flow = function (frame) { + this.outgoing.on_flow(frame.performative); + this.incoming.on_flow(frame.performative); + if (frame.performative.handle !== undefined) { + this._get_link(frame).on_flow(frame); + } + this.connection._register(); +} +Session.prototype._context = function (c) { + var context = c ? c : {}; + context.session = this; + return this.connection._context(context); +}; + +Session.prototype._get_link = function (frame) { + var handle = frame.performative.handle; + var link = this.remote.handles[handle]; + if (!link) { + throw Error('Invalid handle ' + handle); + } + return link; +}; + +Session.prototype.on_detach = function (frame) { + this._get_link(frame).on_detach(frame); + //remove link + var handle = frame.performative.handle; + var link = this.remote.handles[handle]; + delete this.remote.handles[handle]; + delete this.local.handles[link.local.handle]; + delete this.links[link.name]; +}; + +Session.prototype.on_transfer = function (frame) { + this.incoming.on_transfer(frame, this._get_link(frame)); +}; + +module.exports = Session; + +}).call(this,require("buffer").Buffer) +},{"./endpoint.js":2,"./frames.js":3,"./link.js":4,"./log.js":5,"./message.js":6,"./types.js":12,"./util.js":13,"buffer":19,"events":23}],10:[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 terminus = {}; +var by_descriptor = {}; + +function define_terminus(def) { + var c = types.define_composite(def); + terminus[def.name] = c.create; + by_descriptor[Number(c.descriptor.numeric).toString(10)] = c; + by_descriptor[c.descriptor.symbolic] = c; +}; + +terminus.unwrap = function(field) { + if (field && field.descriptor) { + var c = by_descriptor[field.descriptor.value]; + if (c) { + return new c(field.value) + } else { + console.log('Unknown terminus: ' + field.descriptor); + } + } + return null; +}; + +define_terminus( + {name:"source", + code:0x28, + fields: [ + {name:"address", type:"string"}, + {name:"durable", type:"uint", default_value:0}, + {name:"expiry_policy", type:"symbol", default_value:"session-end"}, + {name:"timeout", type:"uint", default_value:0}, + {name:"dynamic", type:"boolean", default_value:false}, + {name:"dynamic_node_properties", type:"symbolic_map"}, + {name:"distribution_mode", type:"symbol"}, + {name:"filter", type:"symbolic_map"}, + {name:"default_outcome", type:"*"}, + {name:"outcomes", type:"symbol", multiple:true}, + {name:"capabilities", type:"symbol", multiple:true} + ] + }); + +define_terminus( + {name:"target", + code:0x29, + fields: [ + {name:"address", type:"string"}, + {name:"durable", type:"uint", default_value:0}, + {name:"expiry_policy", type:"symbol", default_value:"session-end"}, + {name:"timeout", type:"uint", default_value:0}, + {name:"dynamic", type:"boolean", default_value:false}, + {name:"dynamic_node_properties", type:"symbolic_map"}, + {name:"capabilities", type:"symbol", multiple:true} + ] + }); + +module.exports = terminus; + +},{"./types.js":12}],11:[function(require,module,exports){ +(function (Buffer){ +/* + * Copyright 2015 Red Hat Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License
<TRUNCATED> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
