diff --git a/lib/protocol/Connection.js b/lib/protocol/Connection.js
index 1b998e1..b005438 100644
--- a/lib/protocol/Connection.js
+++ b/lib/protocol/Connection.js
@@ -218,6 +218,7 @@ Connection.prototype.open = function open(options, cb) {
function ondata(chunk) {
cleanup();
+ trace('INITIALIZATION REPLY', chunk);
if (!chunk || chunk.length < InitializationReply.LENGTH) {
return cb(invalidInitializationReply());
}
@@ -233,6 +234,7 @@ Connection.prototype.open = function open(options, cb) {
self.port = options['port'];
timeoutObject = setTimeout(onerror, self.initializationTimeout, initializationTimeoutError());
socket.write(initializationRequestBuffer);
+ trace('INITIALIZATION REQUEST', initializationRequestBuffer);
});
socket.once('error', onerror);
socket.on('data', ondata);
@@ -254,6 +256,8 @@ Connection.prototype._addListeners = function _addListeners(socket) {
function ondata(chunk) {
packet.push(chunk);
if (packet.isReady()) {
+ trace('REPLY', packet.getTraceData());
+
if (self._state.sessionId !== packet.header.sessionId) {
self._state.sessionId = packet.header.sessionId;
self._state.packetCount = -1;
@@ -321,9 +325,6 @@ Connection.prototype.send = function send(message, receive) {
message.add(PartKind.CLIENT_INFO, this._clientInfo.getUpdatedProperties());
}
- debug('send', message);
- trace('REQUEST', message);
-
var size = this.packetSizeLimit - PACKET_HEADER_LENGTH;
var buffer = message.toBuffer(size);
if(buffer.length > size) {
@@ -354,6 +355,9 @@ Connection.prototype.send = function send(message, receive) {
if (this._socket) {
this._socket.write(packet);
}
+
+ debug('send', message);
+ trace('REQUEST', packet, this.connectOptions.connectionId);
};
@@ -389,7 +393,6 @@ Connection.prototype.setTransactionFlags = function setTransactionFlags(flags) {
Connection.prototype._parseReplySegment = function _parseReplySegment(buffer) {
var segment = ReplySegment.create(buffer, 0);
- trace(segment.kind === SegmentKind.ERROR ? 'ERROR' : 'REPLY', segment);
return segment.getReply();
};
diff --git a/lib/protocol/MessageBuffer.js b/lib/protocol/MessageBuffer.js
index 84f2603..a36549a 100644
--- a/lib/protocol/MessageBuffer.js
+++ b/lib/protocol/MessageBuffer.js
@@ -24,6 +24,7 @@ function MessageBuffer() {
this.length = 0;
this.header = undefined;
this.data = undefined;
+ this.headerBuffer = undefined;
}
MessageBuffer.prototype.isReady = function () {
@@ -61,12 +62,26 @@ MessageBuffer.prototype.readHeader = function readHeader() {
packetCount: buffer.readUInt32LE(8),
length: buffer.readUInt32LE(12)
};
+ if (util.isTraceOn()) {
+ this.headerBuffer = buffer.slice(0, PACKET_HEADER_LENGTH);
+ }
this.data = buffer.slice(PACKET_HEADER_LENGTH);
this.length -= PACKET_HEADER_LENGTH;
};
+MessageBuffer.prototype.getTraceData = function getTraceData() {
+ if (util.isTraceOn()) {
+ if (Array.isArray(this.data)) {
+ return Buffer.concat([this.headerBuffer].concat(this.data), this.length + PACKET_HEADER_LENGTH);
+ }
+ return Buffer.concat([this.headerBuffer, this.data], this.length + PACKET_HEADER_LENGTH);
+ }
+ return undefined;
+}
+
MessageBuffer.prototype.clear = function clear() {
this.length = 0;
this.header = undefined;
this.data = undefined;
+ this.headerBuffer = undefined;
};
\ No newline at end of file
diff --git a/lib/util/index.js b/lib/util/index.js
index 870d301..e6b985c 100644
--- a/lib/util/index.js
+++ b/lib/util/index.js
@@ -20,6 +20,7 @@ var stream = require('stream');
var path = require('path');
var os = require('os');
var fs = require('fs');
+var packetTracer = require('./trace/PacketTracer');
Object.defineProperties(exports, {
setImmediate: {
@@ -53,10 +54,13 @@ var debug = exports.debuglog('hdb_util');
exports.tracefile = function tracefile() {
var timestamp = Math.floor(Date.now() / 1000);
var filename = 'hdb.trace.' + timestamp + '.log';
- return path.join(os.tmpdir(), filename);
+ return filename;
};
exports._appendFileSync = fs.appendFileSync;
+exports.isTraceOn = function isTraceOn() {
+ return !!process.env.HDB_TRACE;
+}
exports.tracelog = function tracelog() {
if (!process.env.HDB_TRACE) {
return function dummyTracelog() {};
@@ -64,12 +68,16 @@ exports.tracelog = function tracelog() {
var filename = exports.tracefile();
debug('Trace to file', filename);
- return function tracelog(kind, segment) {
- exports._appendFileSync(filename,
- kind + '\n' +
- util.inspect(segment, {
- depth: 9
- }));
+ return function tracelog(kind, buffer, connectionId) {
+ if (kind === 'INITIALIZATION REQUEST') {
+ exports._appendFileSync(filename, packetTracer.parseInitializationRequest(buffer));
+ } else if (kind === 'INITIALIZATION REPLY') {
+ exports._appendFileSync(filename, packetTracer.parseInitializationReply(buffer));
+ } else if (kind === 'REQUEST') {
+ exports._appendFileSync(filename, packetTracer.parseRequest(buffer, connectionId));
+ } else if (kind === 'ERROR' || kind === 'REPLY') {
+ exports._appendFileSync(filename, packetTracer.parseReply(buffer));
+ }
};
};
diff --git a/lib/util/trace/PacketTracer.js b/lib/util/trace/PacketTracer.js
new file mode 100644
index 0000000..5ede4ce
--- /dev/null
+++ b/lib/util/trace/PacketTracer.js
@@ -0,0 +1,315 @@
+// Copyright 2013 SAP AG.
+//
+// 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 zeropad = require('../zeropad');
+var bignum = require('../bignum');
+var MessageType = require('../../protocol/common/MessageType');
+var SegmentKind = require('../../protocol/common/SegmentKind');
+var CommandOption = require('../../protocol/common/CommandOption');
+var PartKind = require('../../protocol/common/PartKind');
+var PartAttributes = require('../../protocol/common/ResultSetAttributes');
+var FunctionCode = require('../../protocol/common/FunctionCode');
+
+var MessageTypeMap = invertToMap(MessageType);
+var SegmentKindMap = invertToMap(SegmentKind);
+var CommandOptionMap = invertToMap(CommandOption);
+var PartKindMap = invertToMap(PartKind);
+var PartAttributesMap = invertToMap(PartAttributes);
+var FunctionCodeMap = invertToMap(FunctionCode);
+
+function invertToMap(object) {
+ var map = new Map();
+ Object.keys(object).forEach(key => {
+ map.set(object[key], key);
+ });
+ return map;
+}
+
+/*
+ The initializationRequestBuffer is formatted as
+ 0xff, 0xff, 0xff, 0xff, // preamble
+ 4, 20, 0, // client version
+ 4, // request major version - 0x04
+ 1, 0, // request minor version - 0x01
+ 0, // reserved
+ 1, // number of options
+ 1, 1 // option_id: 1 - endian, value: 01 - LE (00 - BE)
+*/
+function parseInitializationRequest(buffer) {
+ var traceOutput = new LogWrapper();
+ var log = traceOutput.log.bind(traceOutput);
+ var logData = traceOutput.logData.bind(traceOutput);
+ log(0, " " + getLocalTimestamp());
+ log(4, `INFO REQUEST (${buffer.length} BYTES)`);
+ var requestMajorVersion = buffer.readInt8(7);
+ var requestMinorVersion = buffer.readInt16LE(8);
+ log(2, `REQUEST PROTOCOL VERSION: ${requestMajorVersion}.${requestMinorVersion}`);
+ var numOptions = buffer.readInt8(11);
+ if (numOptions >= 1) {
+ for (var i = 12; i < 12 + 2 * numOptions; i += 2) {
+ if (buffer.readInt8(i) === 1) {
+ if (buffer.readInt8(i + 1) === 1) {
+ log(0, "ENDIAN: LE");
+ } else if (buffer.readInt8(i + 1) === 0) {
+ log(0, "ENDIAN: BE");
+ }
+ }
+ }
+ }
+ traceOutput.indent += 2;
+ logData(formatHexData(traceOutput.indent, buffer, 0, buffer.length));
+ traceOutput.clearIndentLog('');
+ return traceOutput.logStr;
+}
+exports.parseInitializationRequest = parseInitializationRequest;
+
+function parseInitializationReply(buffer) {
+ var traceOutput = new LogWrapper();
+ var log = traceOutput.log.bind(traceOutput);
+ var logData = traceOutput.logData.bind(traceOutput);
+ log(0, " " + getLocalTimestamp());
+ log(4, `INFO REQUEST REPLY (${buffer.length} BYTES)`);
+ traceOutput.indent += 4;
+ logData(formatHexData(traceOutput.indent, buffer, 0, buffer.length));
+ traceOutput.clearIndentLog('');
+ return traceOutput.logStr;
+}
+exports.parseInitializationReply = parseInitializationReply;
+
+function parseRequest(buffer, connectionId) {
+ var traceOutput = new LogWrapper();
+ var log = traceOutput.log.bind(traceOutput);
+ var logData = traceOutput.logData.bind(traceOutput);
+
+ var sessionId = bignum.readInt64LE(buffer, 0);
+ var packetCount = buffer.readUInt32LE(8);
+ var varPartLength = buffer.readUInt32LE(12);
+ var varPartSize = buffer.readUInt32LE(16);
+ var numSegments = buffer.readUInt16LE(20);
+ var packetOptions = buffer.readInt8(22);
+
+ if (connectionId) {
+ log(0, `CONNECTION ID: ${connectionId}`);
+ }
+ log(0, " " + getLocalTimestamp());
+ // Shift indent up 2 spaces
+ traceOutput.indent += 2;
+ if (packetOptions === 2) {
+ var compressionVarpartLength = buffer.readUInt32LE(24);
+ log(0, `COMPRESSED PACKET DECOMPRESSED VARPART LENGTH: ${compressionVarpartLength}`);
+ } else if (packetOptions === 3) {
+ log(0, "SUPPORTS REATTACH");
+ }
+ log(0, `SESSION ID: ${sessionId} PACKET COUNT: ${packetCount}`);
+ log(0, `VARPART LENGTH: ${varPartLength} VARPART SIZE: ${varPartSize}`);
+ log(0, `NO OF SEGMENTS: ${numSegments}`);
+
+ var curSegmentOffset = 32; // Message header is 32 bytes
+ for (var segIndex = 1; segIndex <= numSegments; segIndex++) {
+ var segmentLength = buffer.readInt32LE(curSegmentOffset);
+ var segmentOffset = buffer.readInt32LE(curSegmentOffset + 4);
+ var numParts = buffer.readInt16LE(curSegmentOffset + 8);
+ var segmentNumber = buffer.readInt16LE(curSegmentOffset + 10);
+ var segmentKind = SegmentKindMap.get(buffer.readInt8(curSegmentOffset + 12));
+ var messageType = MessageTypeMap.get(buffer.readInt8(curSegmentOffset + 13));
+ var autoCommit = buffer.readInt8(curSegmentOffset + 14);
+ var commandOptions = readBitOptions(buffer.readInt8(curSegmentOffset + 15), CommandOptionMap);
+
+ log(2, `SEGMENT ${segIndex} OF ${numSegments} MESSAGE TYPE: ${messageType}`);
+ log(2, `LENGTH: ${segmentLength} OFFSET: ${segmentOffset}`);
+ log(0, `NO OF PARTS: ${numParts} NUMBER: ${segmentNumber}`);
+ log(0, `KIND: ${segmentKind} AUTOCOMMIT: ${autoCommit}`);
+ log(0, `OPTIONS: ${commandOptions}`);
+
+ parseParts(buffer, curSegmentOffset, numParts, traceOutput, log, logData);
+
+ curSegmentOffset += segmentLength;
+ }
+
+ traceOutput.clearIndentLog('');
+ return traceOutput.logStr;
+}
+exports.parseRequest = parseRequest;
+
+function parseReply(buffer) {
+ var traceOutput = new LogWrapper();
+ var log = traceOutput.log.bind(traceOutput);
+ var logData = traceOutput.logData.bind(traceOutput);
+
+ var sessionId = bignum.readInt64LE(buffer, 0);
+ var packetCount = buffer.readUInt32LE(8);
+ var varPartLength = buffer.readUInt32LE(12);
+ var varPartSize = buffer.readUInt32LE(16);
+ var numSegments = buffer.readUInt16LE(20);
+ var packetOptions = buffer.readInt8(22);
+
+ log(0, " " + getLocalTimestamp());
+ // Shift indent up 2 spaces
+ traceOutput.indent += 2;
+ if (packetOptions === 2) {
+ var compressionVarpartLength = buffer.readUInt32LE(24);
+ log(0, `COMPRESSED PACKET DECOMPRESSED VARPART LENGTH: ${compressionVarpartLength}`);
+ } else if (packetOptions === 3) {
+ log(0, "INITIATING SESSION REATTACH");
+ }
+ log(0, `SESSION ID: ${sessionId} PACKET COUNT: ${packetCount}`);
+ log(0, `VARPART LENGTH: ${varPartLength} VARPART SIZE: ${varPartSize}`);
+ log(0, `NO OF SEGMENTS: ${numSegments}`);
+
+ var curSegmentOffset = 32; // Message header is 32 bytes
+ for (var segIndex = 1; segIndex <= numSegments; segIndex++) {
+ var segmentLength = buffer.readInt32LE(curSegmentOffset);
+ var segmentOffset = buffer.readInt32LE(curSegmentOffset + 4);
+ var numParts = buffer.readInt16LE(curSegmentOffset + 8);
+ var segmentNumber = buffer.readInt16LE(curSegmentOffset + 10);
+ var segmentKind = SegmentKindMap.get(buffer.readInt8(curSegmentOffset + 12));
+ var functionCode = FunctionCodeMap.get(buffer.readInt16LE(curSegmentOffset + 14));
+
+ log(2, `SEGMENT ${segIndex}`);
+ log(2, `LENGTH: ${segmentLength} OFFSET: ${segmentOffset}`);
+ log(0, `NO OF PARTS: ${numParts} NUMBER: ${segmentNumber}`);
+ log(0, `KIND: ${segmentKind}`);
+ log(0, `FUNCTION CODE: ${functionCode}`);
+
+ parseParts(buffer, curSegmentOffset, numParts, traceOutput, log, logData);
+
+ curSegmentOffset += segmentLength;
+ }
+
+ traceOutput.clearIndentLog('');
+ return traceOutput.logStr;
+}
+exports.parseReply = parseReply;
+
+function parseParts(buffer, curSegmentOffset, numParts, traceOutput, log, logData) {
+ var curPartOffset = curSegmentOffset + 24;
+ for (var partIndex = 1; partIndex <= numParts; partIndex++) {
+ var partKind = PartKindMap.get(buffer.readInt8(curPartOffset));
+ var partAttributes = readBitOptions(buffer.readInt8(curPartOffset + 1), PartAttributesMap);
+ var argumentCount = buffer.readInt16LE(curPartOffset + 2);
+ if (argumentCount === -1) {
+ argumentCount = buffer.readInt32LE(curPartOffset + 4);
+ }
+ var bufferLength = buffer.readInt32LE(curPartOffset + 8);
+ var alignedBufferLength = alignLength(bufferLength, 8);
+ var bufferSize = buffer.readInt32LE(curPartOffset + 12);
+
+ log(0, `PART ${partIndex} ${partKind}`);
+ log(2, `LENGTH: ${bufferLength} SIZE: ${bufferSize}`);
+ log(0, `ARGUMENTS: ${argumentCount}`);
+ log(0, `ATTRIBUTES: ${partAttributes}`);
+ log(0, "DATA:");
+
+ if (buffer.readInt8(curPartOffset) === PartKind.AUTHENTICATION) {
+ // Do not log authentication information
+ log(0, "[AUTHENTICATION INFORMATION]");
+ } else if (bufferLength > 0) {
+ // Part header is 16 bytes
+ logData(formatHexData(traceOutput.indent, buffer, curPartOffset + 16, curPartOffset + 16 + bufferLength));
+ }
+
+ // Shift indent back 2 spaces
+ traceOutput.indent -= 2;
+ curPartOffset += 16 + alignedBufferLength;
+ }
+}
+
+function formatBufferRow(indent, buffer, curIndex, offset, end) {
+ var calcEnd = offset + curIndex + 16;
+ var trueEnd;
+ if (end < calcEnd) trueEnd = end;
+ else trueEnd = calcEnd;
+ var hexEncodingStr = "";
+ var humanReadableStr = "";
+
+ for (var i = offset + curIndex; i < trueEnd; i++) {
+ hexEncodingStr += zeropad.lpad(2, buffer[i].toString(16).toUpperCase());
+ if (i < trueEnd - 1) hexEncodingStr += " ";
+
+ if (buffer[i] >= 32 && buffer[i] <= 127) {
+ humanReadableStr += String.fromCharCode(buffer[i]);
+ } else {
+ humanReadableStr += '.';
+ }
+ }
+
+ var indexStr = curIndex.toString(16).toUpperCase() + "|";
+ indexStr = indexStr.padStart(indent, ' ');
+
+ // Pad the end of the hex encoding to be 47 in length, the length when
+ // all 16 bytes are set. Similarly, pad the humanReadableStr to 16
+ return indexStr + hexEncodingStr.padEnd(47, ' ') + "|" + humanReadableStr.padEnd(16) + "|";
+}
+
+function formatHexData(indent, buffer, offset, end) {
+ // Split the data into rows of 16 bytes
+ var hexDataStr = "";
+ var endIndex = end - offset;
+ for (var i = 0; i < endIndex; i += 16) {
+ hexDataStr += formatBufferRow(indent, buffer, i, offset, end) + "\n";
+ }
+ return hexDataStr;
+}
+
+function getLocalTimestamp() {
+ var curDate = new Date();
+ return curDate.getFullYear() + '-'
+ + zeropad.lpad(2, curDate.getMonth() + 1) + '-'
+ + zeropad.lpad(2, curDate.getDate()) + ' '
+ + zeropad.lpad(2, curDate.getHours()) + ':'
+ + zeropad.lpad(2, curDate.getMinutes()) + ':'
+ + zeropad.lpad(2, curDate.getSeconds()) + '.'
+ + zeropad.lpad(3, curDate.getMilliseconds());
+}
+
+function readBitOptions(options, optionMap) {
+ var firstOption = true;
+ var result = "(";
+ optionMap.forEach((value, key) => {
+ if (options & key) {
+ if (firstOption) firstOption = false;
+ else result += "|";
+ result += value;
+ }
+ });
+ result += ")";
+ return result;
+}
+
+function alignLength(length, alignment) {
+ if (length % alignment === 0) {
+ return length;
+ }
+ return length + alignment - length % alignment;
+}
+
+function LogWrapper() {
+ this.logStr = "";
+ this.indent = 0;
+}
+
+LogWrapper.prototype.log = function log(indentChange, message) {
+ this.indent += indentChange;
+ this.logStr += ' '.repeat(this.indent) + message + "\n";
+}
+
+LogWrapper.prototype.clearIndentLog = function log(message) {
+ this.indent = 0;
+ this.logStr += message + "\n\n";
+}
+
+LogWrapper.prototype.logData = function logData(dataStr) {
+ this.logStr += dataStr;
+}
diff --git a/test/fixtures/packetTraceData.js b/test/fixtures/packetTraceData.js
new file mode 100644
index 0000000..6b03cf3
--- /dev/null
+++ b/test/fixtures/packetTraceData.js
@@ -0,0 +1,181 @@
+// Copyright 2013 SAP AG.
+//
+// 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';
+
+exports.AUTHREQUEST = {
+ packet: new Buffer(
+ '0000000000000000' + // Session id
+ '00000000' + // Packet count
+ '70010000' + // Varpart length
+ 'e0ff0f00' + // Varpart size
+ '0100' + // Number of segments
+ '00000000000000000000' + // Extra options
+ '70010000' + // Segment length
+ '00000000' + // Segment offset
+ '0300' + // Number of parts
+ '0100' + // Segment number
+ '01' + // Segment kind
+ '41' + // Message type
+ '00' + // Auto commit
+ '00' + // Command options
+ '0000000000000000' + // Filler
+ '1d' + // Part kind
+ '00' + // Part attributes
+ '0300' + // Argument count
+ '00000000' + // Big argument count
+ '28000000' + // Part buffer length
+ 'b8ff0f00' + // Buffer size
+ '011d1200322e32332e32342e31373333353138383135021d060053514c444243031d04006e6f6465' + // Part data
+ '43' + // Part kind
+ '00' + // Part attributes
+ '0000' + // Argument count
+ '00000000' + // Big argument count
+ '00000000' + // Part buffer length
+ '80ff0f00' + // Buffer size
+ '21' + // Part kind
+ '00' + // Part attributes
+ '0100' + // Argument count
+ '00000000' + // Big argument count
+ 'fb000000' + // Part buffer length
+ '70ff0f00' + // Buffer size
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000', 'hex'), // Part data
+ trace: [
+ "",
+ " SESSION ID: 0 PACKET COUNT: 0",
+ " VARPART LENGTH: 368 VARPART SIZE: 1048544",
+ " NO OF SEGMENTS: 1",
+ " SEGMENT 1 OF 1 MESSAGE TYPE: AUTHENTICATE",
+ " LENGTH: 368 OFFSET: 0",
+ " NO OF PARTS: 3 NUMBER: 1",
+ " KIND: REQUEST AUTOCOMMIT: 0",
+ " OPTIONS: ()",
+ " PART 1 CLIENT_CONTEXT",
+ " LENGTH: 40 SIZE: 1048504",
+ " ARGUMENTS: 3",
+ " ATTRIBUTES: ()",
+ " DATA:",
+ " 0|01 1D 12 00 32 2E 32 33 2E 32 34 2E 31 37 33 33|....2.23.24.1733|",
+ " 10|35 31 38 38 31 35 02 1D 06 00 53 51 4C 44 42 43|518815....SQLDBC|",
+ " 20|03 1D 04 00 6E 6F 64 65 |....node |",
+ " PART 2 DB_CONNECT_INFO",
+ " LENGTH: 0 SIZE: 1048448",
+ " ARGUMENTS: 0",
+ " ATTRIBUTES: ()",
+ " DATA:",
+ " PART 3 AUTHENTICATION",
+ " LENGTH: 251 SIZE: 1048432",
+ " ARGUMENTS: 1",
+ " ATTRIBUTES: ()",
+ " DATA:",
+ " [AUTHENTICATION INFORMATION]",
+ ""
+ ],
+};
+
+exports.REPLY = {
+ packet: new Buffer(
+ '54d871a5a3960300' + // Session id
+ '05000000' + // Packet count
+ '68010000' + // Varpart length
+ '10750000' + // Varpart size
+ '0100' + // Number of segments
+ '00000000000000000000' + // Extra options
+ '68010000' + // Segment length
+ '00000000' + // Segment offset
+ '0300' + // Number of parts
+ '0100' + // Segment number
+ '02' + // Segment kind
+ '00' + // Filler
+ '0500' + // Function code
+ '0000000000000000' + // Filler
+ '0d' + // Part kind
+ '00' + // Part attributes
+ '0100' + // Argument count
+ '00000000' + // Big argument count
+ '08000000' + // Part buffer length
+ '40010000' + // Buffer size
+ 'faa8000046170300' + // Part data
+ '27' + // Part kind
+ '00' + // Part attributes
+ '0400' + // Argument count
+ '00000000' + // Big argument count
+ 'd6000000' + // Part buffer length
+ '28010000' + // Buffer size
+ '0121b4000100000000000fd0920b0000000000002373000000000000a43d00000000000000000000' +
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000fd0edfe0fd0ffffffffffffff7f000000000000000000000000000040b001000000' +
+ '000000000000000000000000fce37115b7620100851299140204b6050000000000000704e1030000' +
+ '00000000080468aa0000000000000000' + // Part data
+ '05' + // Part kind
+ '11' + // Part attributes
+ '0200' + // Argument count
+ '00000000' + // Big argument count
+ '3a000000' + // Part buffer length
+ '40000000' + // Buffer size
+ '1a4865726520697320736f6d6520737472696e6720746f206164641e486572652069732061207365' +
+ '636f6e6420737472696e6720746f20616464000000000000', 'hex'), // Part data
+ trace: [
+ "",
+ " SESSION ID: 1010054529669204 PACKET COUNT: 5",
+ " VARPART LENGTH: 360 VARPART SIZE: 29968",
+ " NO OF SEGMENTS: 1",
+ " SEGMENT 1",
+ " LENGTH: 360 OFFSET: 0",
+ " NO OF PARTS: 3 NUMBER: 1",
+ " KIND: REPLY",
+ " FUNCTION CODE: SELECT",
+ " PART 1 RESULT_SET_ID",
+ " LENGTH: 8 SIZE: 320",
+ " ARGUMENTS: 1",
+ " ATTRIBUTES: ()",
+ " DATA:",
+ " 0|FA A8 00 00 46 17 03 00 |....F... |",
+ " PART 2 STATEMENT_CONTEXT",
+ " LENGTH: 214 SIZE: 296",
+ " ARGUMENTS: 4",
+ " ATTRIBUTES: ()",
+ " DATA:",
+ " 0|01 21 B4 00 01 00 00 00 00 00 0F D0 92 0B 00 00|.!..............|",
+ " 10|00 00 00 00 23 73 00 00 00 00 00 00 A4 3D 00 00|....#s.......=..|",
+ " 20|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00|................|",
+ " 30|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00|................|",
+ " 40|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00|................|",
+ " 50|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00|................|",
+ " 60|00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00|................|",
+ " 70|00 00 00 00 00 00 00 00 00 00 00 00 00 00 0F D0|................|",
+ " 80|ED FE 0F D0 FF FF FF FF FF FF FF 7F 00 00 00 00|...........\x7F....|",
+ " 90|00 00 00 00 00 00 00 00 00 00 40 B0 01 00 00 00|..........@.....|",
+ " A0|00 00 00 00 00 00 00 00 00 00 00 00 FC E3 71 15|..............q.|",
+ " B0|B7 62 01 00 85 12 99 14 02 04 B6 05 00 00 00 00|.b..............|",
+ " C0|00 00 07 04 E1 03 00 00 00 00 00 00 08 04 68 AA|..............h.|",
+ " D0|00 00 00 00 00 00 |...... |",
+ " PART 3 RESULT_SET",
+ " LENGTH: 58 SIZE: 64",
+ " ARGUMENTS: 2",
+ " ATTRIBUTES: (LAST|CLOSED)",
+ " DATA:",
+ " 0|1A 48 65 72 65 20 69 73 20 73 6F 6D 65 20 73 74|.Here is some st|",
+ " 10|72 69 6E 67 20 74 6F 20 61 64 64 1E 48 65 72 65|ring to add.Here|",
+ " 20|20 69 73 20 61 20 73 65 63 6F 6E 64 20 73 74 72| is a second str|",
+ " 30|69 6E 67 20 74 6F 20 61 64 64 |ing to add |",
+ "",
+ ]
+}
diff --git a/test/util.index.js b/test/util.index.js
index 23c9e23..80dea0b 100644
--- a/test/util.index.js
+++ b/test/util.index.js
@@ -99,7 +99,11 @@ describe('Util', function () {
});
it('should return a dummy tracelog function', function () {
+ // Temporarily unset the HDB_TRACE environment variable
+ var hdbTrace = process.env.HDB_TRACE;
+ delete process.env.HDB_TRACE;
var tracelog = util.tracelog();
+ process.env.HDB_TRACE = hdbTrace;
(!tracelog()).should.be.ok;
tracelog.name.should.equal('dummyTracelog');
});
diff --git a/test/util.trace.js b/test/util.trace.js
new file mode 100644
index 0000000..9f0389d
--- /dev/null
+++ b/test/util.trace.js
@@ -0,0 +1,60 @@
+// Copyright 2013 SAP AG.
+//
+// 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 packetTracer = require('../lib/util/trace/PacketTracer')
+
+var data = require('./fixtures/packetTraceData');
+
+describe('Util', function () {
+
+ describe('#trace', function () {
+
+ it('should trace an authenticate request', function (done) {
+ var test = data.AUTHREQUEST;
+ var traceStr = packetTracer.parseRequest(test.packet);
+ var traceLines = traceStr.split('\n');
+ // Remove trailing new lines
+ while(traceLines.length > 0 && traceLines[traceLines.length - 1] === '') {
+ traceLines.pop();
+ }
+ traceLines.length.should.eql(test.trace.length);
+ // Check without timestamp next to
+ traceLines[0].should.startWith(test.trace[0]);
+ for (var i = 1; i < test.trace.length; i++) {
+ traceLines[i].should.eql(test.trace[i]);
+ }
+ done();
+ });
+
+ it('should trace a select reply', function (done) {
+ var test = data.REPLY;
+ var traceStr = packetTracer.parseReply(test.packet);
+ var traceLines = traceStr.split('\n');
+ // Remove trailing new lines
+ while(traceLines.length > 0 && traceLines[traceLines.length - 1] === '') {
+ traceLines.pop();
+ }
+ traceLines.length.should.eql(test.trace.length);
+ // Check without timestamp next to
+ traceLines[0].should.startWith(test.trace[0]);
+ for (var i = 1; i < test.trace.length; i++) {
+ traceLines[i].should.eql(test.trace[i]);
+ }
+ done();
+ });
+
+ });
+
+});