UNPKG

ti-debug

Version:

Server-side components for WebKit Remote Debugging

145 lines (113 loc) 4.66 kB
var xml2json = require('xml2json'); /* ************************************************************************** */ // // Accept connections to the DBGp client. // // The DBGp protocol is documented at http://xdebug.org/docs-dbgp.php. Generally // this block takes care of receiving the data and passing on the messages once // they are fully available. // module.exports.upgrade = function (socket, logger) { socket.tidebugLogger = logger; // Used for unique transaction IDs, keep track of how many commands we send. socket.tidebugSendCount = 0; // When we send messages to the backend, we retain callbacks for invoking // when we receive replies. socket.tidebugCallbacks = {}; // Use a buffer to hold partial messages as we receive them. socket.tidebugRecvDataBuffer = ''; socket.setEncoding('utf8'); socket.tidebugRecvMessage = module.exports.socketRecvMessage.bind(socket); socket.tidebugRecvData = module.exports.socketRecvData.bind(socket); socket.sendMessage = module.exports.socketSendMessage.bind(socket); socket.on('data', socket.tidebugRecvData); }; // Create a function for converting a text-based message into a usable JSON // representation and then call the appropriate callback or event that can // receive the message. module.exports.socketRecvMessage = function (raw) { // doesn't like the xml header - @todo this should probably be dynamic var str = raw.replace(/^(<\?xml[^>]+>\s*)/, ''); try { obj = xml2json.toJson(str, { object : true }); } catch (e) { if (this.tidebugLogger) { this.tidebugLogger.error(str); } throw e; } if (this.tidebugLogger) { this.tidebugLogger.debug('[dbgp#' + this.remoteAddress + ':' + this.remotePort + '] recv ' + str); } if (obj.response && obj.response.transaction_id && this.tidebugCallbacks[obj.response.transaction_id]) { this.tidebugCallbacks[obj.response.transaction_id](obj.response); delete this.tidebugCallbacks[obj.response.transaction_id]; } else { this.emit('payload', obj, raw); } } // Create a function which handles additional message data that we can add // to our buffer. Always try to parse the extra data to see if we have a // full message that we can pass on to `recvMessage`. module.exports.socketRecvData = function (data) { if (data) { this.tidebugRecvDataBuffer += data; } var nil = false; for (var i = 0; i < 16; i ++) { if (this.tidebugRecvDataBuffer.charCodeAt(i) == 0) { nil = i; break; } } if (nil === false) { // waiting for more data return; } size = parseInt(this.tidebugRecvDataBuffer.slice(0, nil)); // we received multiple messages... if (this.tidebugRecvDataBuffer.length >= (size + nil)) { var message = this.tidebugRecvDataBuffer.slice(nil + 1, nil + 1 + size); this.tidebugRecvDataBuffer = this.tidebugRecvDataBuffer.slice(nil + 1 + size + 1); // handle the first message... this.tidebugRecvMessage(message); if (this.tidebugRecvDataBuffer.length) { // and continue parsing... this.tidebugRecvData(); } } }; // Create a new function that our inspector agents can use to communicate // with the DBGp engine. // // The first argument should always be a string of the DBGp command. // The next argument can be a hash of command options to pass. // The next argument can be a string of data for the command (raw). // The final argument should always be a callback function for handling the // response. // module.exports.socketSendMessage = function () { var vargs = [].slice.apply(arguments); var command = vargs.shift(); var opts = typeof vargs[0] == 'object' ? vargs.shift() : {}; var data = typeof vargs[0] == 'string' ? vargs.shift() : null; var callback = vargs.shift(); this.tidebugSendCount += 1; var write = command + ' -i ' + this.tidebugSendCount; for (var k in opts) { write += ' -' + k + ' ' + JSON.stringify(opts[k]); } if (data !== null) { // base64 the data argument if we have it... write += ' -- ' + new Buffer(data).toString('base64'); } if (this.tidebugLogger) { this.tidebugLogger.debug('[dbgp#' + this.remoteAddress + ':' + this.remotePort + '] send ' + write); } // Finish off our command with null... write += '\0'; // Save our callback for later... this.tidebugCallbacks[this.tidebugSendCount] = callback; // Finally send off the command... this.write(write); };