First attempt at better error handling, also some general cleanup

This commit is contained in:
roblabla 2015-08-27 20:48:45 +00:00
parent 9d7e4b5943
commit 7e145f763d
4 changed files with 109 additions and 51 deletions

View file

@ -1,4 +1,4 @@
var getField = require("../utils").getField; var { getField, tryCatch, addErrorField } = require("../utils");
var debug = require("../debug"); var debug = require("../debug");
module.exports = { module.exports = {
@ -25,14 +25,26 @@ function readArray(buffer, offset, typeArgs, rootNode) {
else if (typeof typeArgs.count !== "undefined") else if (typeof typeArgs.count !== "undefined")
count = getField(typeArgs.count, rootNode); count = getField(typeArgs.count, rootNode);
else if (typeof typeArgs.countType !== "undefined") { else if (typeof typeArgs.countType !== "undefined") {
var countResults = this.read(buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode); var countResults;
tryCatch(() => {
countResults = this.read(buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}, (e) => {
addErrorField(e, "$count");
throw e;
});
results.size += countResults.size; results.size += countResults.size;
offset += countResults.size; offset += countResults.size;
count = countResults.value; count = countResults.value;
} else // TODO : broken schema, should probably error out. } else // TODO : broken schema, should probably error out.
count = 0; count = 0;
for(var i = 0; i < count; i++) { for(var i = 0; i < count; i++) {
var readResults = this.read(buffer, offset, typeArgs.type, rootNode); var readResults;
tryCatch(() => {
readResults = this.read(buffer, offset, typeArgs.type, rootNode);
}, (e) => {
addErrorField(e, i);
throw e;
});
results.size += readResults.size; results.size += readResults.size;
offset += readResults.size; offset += readResults.size;
results.value.push(readResults.value); results.value.push(readResults.value);
@ -43,11 +55,21 @@ function readArray(buffer, offset, typeArgs, rootNode) {
function writeArray(value, buffer, offset, typeArgs, rootNode) { function writeArray(value, buffer, offset, typeArgs, rootNode) {
if (typeof typeArgs.count === "undefined" && if (typeof typeArgs.count === "undefined" &&
typeof typeArgs.countType !== "undefined") { typeof typeArgs.countType !== "undefined") {
offset = this.write(value.length, buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode); tryCatch(() => {
offset = this.write(value.length, buffer, offset, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}, (e) => {
addErrorField(e, "$count");
throw e;
});
} else if (typeof typeArgs.count === "undefined") { // Broken schema, should probably error out } else if (typeof typeArgs.count === "undefined") { // Broken schema, should probably error out
} }
for(var index in value) { for(var index in value) {
offset = this.write(value[index], buffer, offset, typeArgs.type, rootNode); tryCatch(() => {
offset = this.write(value[index], buffer, offset, typeArgs.type, rootNode);
}, (e) => {
addErrorField(e, i);
throw e;
});
} }
return offset; return offset;
} }
@ -56,10 +78,20 @@ function sizeOfArray(value, typeArgs, rootNode) {
var size = 0; var size = 0;
if (typeof typeArgs.count === "undefined" && if (typeof typeArgs.count === "undefined" &&
typeof typeArgs.countType !== "undefined") { typeof typeArgs.countType !== "undefined") {
size = this.sizeOf(value.length, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode); tryCatch(() => {
size = this.sizeOf(value.length, { type: typeArgs.countType, typeArgs: typeArgs.countTypeArgs }, rootNode);
}, (e) => {
addErrorField(e, "$count");
throw e;
});
} }
for(var index in value) { for(var index in value) {
size += this.sizeOf(value[index], typeArgs.type, rootNode); tryCatch(() => {
size += this.sizeOf(value[index], typeArgs.type, rootNode);
}, (e) => {
addErrorField(e, i);
throw e;
});
} }
return size; return size;
} }
@ -70,16 +102,16 @@ function readContainer(buffer, offset, typeArgs, rootNode) {
value: {}, value: {},
size: 0 size: 0
}; };
// BLEIGH. Huge hack because I have no way of knowing my current name.
// TODO : either pass fieldInfo instead of typeArgs as argument (bleigh), or send name as argument (verybleigh).
// TODO : what I do inside of roblabla/Protocols is have each "frame" create a new empty slate with just a "super" object pointing to the parent.
var backupThis = rootNode.this; var backupThis = rootNode.this;
rootNode.this = results.value; rootNode.this = results.value;
for(var index in typeArgs) { for(var index in typeArgs) {
var readResults = this.read(buffer, offset, typeArgs[index].type, rootNode); var readResults;
if(readResults == null || readResults.value == null) { tryCatch(() => {
continue; readResults = this.read(buffer, offset, typeArgs[index].type, rootNode);
} }, (e) => {
addErrorField(e, index);
throw e;
});
results.size += readResults.size; results.size += readResults.size;
offset += readResults.size; offset += readResults.size;
results.value[typeArgs[index].name] = readResults.value; results.value[typeArgs[index].name] = readResults.value;
@ -92,7 +124,12 @@ function writeContainer(value, buffer, offset, typeArgs, rootNode) {
var backupThis = rootNode.this; var backupThis = rootNode.this;
rootNode.this = value; rootNode.this = value;
for(var index in typeArgs) { for(var index in typeArgs) {
offset = this.write(value[typeArgs[index].name], buffer, offset, typeArgs[index].type, rootNode); tryCatch(() => {
offset = this.write(value[typeArgs[index].name], buffer, offset, typeArgs[index].type, rootNode);
}, (e) => {
addErrorField(e, index);
throw e;
});
} }
rootNode.this = backupThis; rootNode.this = backupThis;
return offset; return offset;
@ -103,7 +140,12 @@ function sizeOfContainer(value, typeArgs, rootNode) {
var backupThis = rootNode.this; var backupThis = rootNode.this;
rootNode.this = value; rootNode.this = value;
for(var index in typeArgs) { for(var index in typeArgs) {
size += this.sizeOf(value[typeArgs[index].name], typeArgs[index].type, rootNode); tryCatch(() => {
size += this.sizeOf(value[typeArgs[index].name], typeArgs[index].type, rootNode);
}, (e) => {
addErrorField(e, index);
throw e;
});
} }
rootNode.this = backupThis; rootNode.this = backupThis;
return size; return size;

View file

@ -3,7 +3,7 @@ var protocol = require("../protocol");
var Transform = require("readable-stream").Transform; var Transform = require("readable-stream").Transform;
var debug = require("../debug"); var debug = require("../debug");
var assert = require('assert'); var assert = require('assert');
var { getFieldInfo } = require('../utils'); var { getFieldInfo, tryCatch, addErrorField } = require('../utils');
module.exports.createSerializer = function(obj) { module.exports.createSerializer = function(obj) {
return new Serializer(obj); return new Serializer(obj);
@ -72,13 +72,12 @@ function createPacketBuffer(packetId, state, params, isServer) {
var packet = get(packetId, state, !isServer); var packet = get(packetId, state, !isServer);
assert.notEqual(packet, null); assert.notEqual(packet, null);
packet.forEach(function(fieldInfo) { packet.forEach(function(fieldInfo) {
try { tryCatch(() => {
length += proto.sizeOf(params[fieldInfo.name], fieldInfo.type, params); length += proto.sizeOf(params[fieldInfo.name], fieldInfo.type, params);
} catch(e) { }, (e) => {
console.log("fieldInfo : " + JSON.stringify(fieldInfo)); e.message = "sizeOf error for " + e.field + " : " + e.message;
console.log("params : " + JSON.stringify(params));
throw e; throw e;
} });
}); });
length += utils.varint[2](packetId); length += utils.varint[2](packetId);
var size = length;// + utils.varint[2](length); var size = length;// + utils.varint[2](length);
@ -90,7 +89,12 @@ function createPacketBuffer(packetId, state, params, isServer) {
// TODO : This check belongs to the respective datatype. // TODO : This check belongs to the respective datatype.
if(typeof value === "undefined" && fieldInfo.type != "count") if(typeof value === "undefined" && fieldInfo.type != "count")
debug(new Error("Missing Property " + fieldInfo.name).stack); debug(new Error("Missing Property " + fieldInfo.name).stack);
offset = proto.write(value, buffer, offset, fieldInfo.type, params); tryCatch(() => {
offset = proto.write(value, buffer, offset, fieldInfo.type, params);
}, (e) => {
e.message = "Write error for " + e.field + " : " + e.message;
throw e;
});
}); });
return buffer; return buffer;
} }
@ -125,11 +129,7 @@ function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": tr
var packetInfo = get(packetId, state, isServer); var packetInfo = get(packetId, state, isServer);
if(packetInfo === null) { if(packetInfo === null) {
return { throw new Error("Unrecognized packetId: " + packetId + " (0x" + packetId.toString(16) + ")")
error: new Error("Unrecognized packetId: " + packetId + " (0x" + packetId.toString(16) + ")"),
buffer: buffer,
results: results
};
} else { } else {
var packetName = packetNames[state][isServer ? "toServer" : "toClient"][packetId]; var packetName = packetNames[state][isServer ? "toServer" : "toClient"][packetId];
debug("read packetId " + state + "." + packetName + " (0x" + packetId.toString(16) + ")"); debug("read packetId " + state + "." + packetName + " (0x" + packetId.toString(16) + ")");
@ -139,31 +139,16 @@ function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": tr
for(i = 0; i < packetInfo.length; ++i) { for(i = 0; i < packetInfo.length; ++i) {
fieldInfo = packetInfo[i]; fieldInfo = packetInfo[i];
readResults = proto.read(buffer, cursor, fieldInfo.type, results); readResults = proto.read(buffer, cursor, fieldInfo.type, results);
/* A deserializer cannot return null anymore. Besides, proto.read() returns if(readResults === null)
* null when the condition is not fulfilled. throw new Error("A reader returned null. This is _not_ normal");
if (!!!readResults) { if(readResults.error)
var error = new Error("A deserializer returned null"); throw new Error("A reader returned an error using the old method.");
error.packetId = packetId; if (readResults.value != null)
error.fieldInfo = fieldInfo.name; results[fieldInfo.name] = readResults.value;
return {
size: length + lengthField.size,
error: error,
results: results
};
}*/
// TODO : investigate readResults returning null : shouldn't happen.
// When there is not enough data to read, we should return an error.
// As a general rule, it would be a good idea to introduce a whole bunch
// of new error classes to differenciate the errors.
if(readResults === null || readResults.value == null) continue;
if(readResults.error) {
return readResults;
}
results[fieldInfo.name] = readResults.value;
cursor += readResults.size; cursor += readResults.size;
} }
if(buffer.length > cursor) if(buffer.length > cursor)
debug("Too much data to read for packetId: " + packetId + " (0x" + packetId.toString(16) + ")"); throw new Error("Packet data not entirely read");
debug(results); debug(results);
return { return {
results: results, results: results,

View file

@ -1,6 +1,8 @@
module.exports = { module.exports = {
getField: getField, getField: getField,
getFieldInfo: getFieldInfo, getFieldInfo: getFieldInfo,
addErrorField: addErrorField,
tryCatch: tryCatch,
}; };
function getField(countField, rootNode) { function getField(countField, rootNode) {
@ -22,3 +24,14 @@ function getFieldInfo(fieldInfo) {
else else
throw new Error("Not a fieldinfo"); throw new Error("Not a fieldinfo");
} }
function addErrorField(e, field) {
if (e.field)
e.field = field + "." + e.field;
else
e.field = field;
}
function tryCatch(tryfn, catchfn) {
try { tryfn(); } catch (e) { catchfn(e); }
}

View file

@ -53,6 +53,12 @@ var defaultServerProps = {
'motd': 'A Minecraft Server', 'motd': 'A Minecraft Server',
}; };
function evalCount(count, fields) {
if(fields[count["field"]] in count["map"])
return count["map"][fields[count["field"]]];
return count["default"];
}
var values = { var values = {
'int': 123456, 'int': 123456,
'short': -123, 'short': -123,
@ -63,7 +69,19 @@ var values = {
'string': "hi hi this is my client string", 'string': "hi hi this is my client string",
'buffer': new Buffer(8), 'buffer': new Buffer(8),
'array': function(typeArgs, packet) { 'array': function(typeArgs, packet) {
return [getValue(typeArgs.type, packet)]; var count;
if (typeof typeArgs.count === "object")
count = evalCount(typeArgs.count, packet);
else if (typeof typeArgs.count !== "undefined")
count = getField(typeArgs.count, rootNode);
else if (typeof typeArgs.countType !== "undefined")
count = 1;
var arr = [];
while (count > 0) {
arr.push(getValue(typeArgs.type, packet));
count--;
}
return arr;
}, },
'container': function(typeArgs, packet) { 'container': function(typeArgs, packet) {
var results = {}; var results = {};