* Returns the PDU if successfull or null if not or an exception
* if the PDU is incorrect.
*/
private final PDU tryGetUnprocessedPDU(Unprocessed unprocessed) throws UnknownCommandIdException, PDUException {
debug.write(DRXTX, "trying to create pdu from unprocessed buffer");
PDU pdu = null;
ByteBuffer unprocBuffer = unprocessed.getUnprocessed();
try {
pdu = PDU.createPDU(unprocBuffer);
unprocessed.check();
} catch (HeaderIncompleteException e) {
// the header wasn't received completly, we will try to
// receive the rest next time
debug.write(DRXTXD, "incomplete message header, will wait for the rest.");
unprocessed.setHasUnprocessed(false); // as it's incomplete - wait for new data
unprocessed.setExpected(Data.PDU_HEADER_SIZE);
} catch (MessageIncompleteException e) {
if (messageIncompleteRetryCount > 1) { // paolo@bulksms.com
messageIncompleteRetryCount = 0;
event.write("Giving up on incomplete messages - probably garbage in unprocessed buffer. Flushing unprocessed buffer.");
unprocessed.reset();
}
// the message wasn't received completly, less bytes than command
// length has been received, will try to receive the rest next time
debug.write(DRXTXD, "incomplete message, will wait for the rest.");
unprocessed.setHasUnprocessed(false); // as it's incomplete - wait for new data
unprocessed.setExpected(Data.PDU_HEADER_SIZE);
messageIncompleteRetryCount++;
} catch (UnknownCommandIdException e) {
// message with invalid id was received, should send generic_nack
debug.write(DRXTX, "unknown pdu, might remove from unprocessed buffer. CommandId=" + e.getCommandId());
if (e.getCommandLength() <= unprocBuffer.length()) {
// have already enough to remove
try {
unprocBuffer.removeBytes(e.getCommandLength());
} catch (NotEnoughDataInByteBufferException e1) {
// can't happen, we've checked it above
throw new Error("Not enough data in buffer even if previously checked that there was enough.");
}
unprocessed.check();
throw e; // caller will decide what to do
}
// paolo@bulksms.com: added this: see why in caller. Advantage: lets
// us trap garbage PDUs that break things by being trapped in the
// unprocessed buffer eternally. Disadvantage: if this was a valid
// (well-formed) PDU with an unknown command id AND it was not fully
// read into the buffer in one go, then:
// 1) we will respond to the part _was_ already read (which we know
// contains at least a header - see code in PDU) with an error, which
// is fine.
// 2) when we receive the second part of the incomplete PDU, it will
// effectively seem to us to be an invalid PDU itself. It could be
// processed as follows:
// - as an unknown command_id again, which is fine; or
// - as an incomplete message, which is a problem, because it will
// then have the subsequent PDU tacked to the end of it, and
// that PDU will then be discarded as well (and almost certainly
// discarded via an UnknownCommandIdException again).
throw e;
} catch (PDUException e) {
// paolo@bulksms.com: safer to catch all other PDU exceptions and force
// force a check() here - some exception in parsing should not be allowed
// to leave ghost data in the Unprocessed buffer (even though this is now
// less likely after improvements to PDU.createPDU()):
unprocessed.check();
throw e;
}
/* paolo@bulksms.com: concerned that this is too broad, and will
stop useful exceptions from being passed back up the call stack,
so disabling for now:
} catch (Exception e) {
debug.write(DRXTX, "Exception catched: " + e.toString());
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
debug.write(DRXTX, stringWriter.toString());
}
*/
if (pdu != null) {
debug.write(DRXTX, "received complete pdu" + pdu.debugString());
debug.write(DRXTX, "there is " + unprocBuffer.length() + " bytes left in unprocessed buffer");
}
// unprocessed.check();
return pdu;
}