Tell a refused PDU apart from a session that failed (#82)
* Regression tests for telling a refused PDU from a failed session * Export PduRefusedError so a refusal can be told from a session failure * Document telling a refused PDU from a session failure * Share the malformed-PDU fixtures and pin that a dead stream is no refusal * Say once what a refusal is, and why the header type is published * Fold the refused-response test into the one that already staged it * Drop the refusal-rate claim the interop finding does not support * Build a raw PDU one way, sequence number included * Follow PduRefusedError into pdu-refusal.ts
This commit is contained in:
@@ -265,6 +265,41 @@ Runtime failures on a live connection arrive as `sessionError` and `serverError`
|
||||
deliberately not called `error`: Node turns an unhandled `error` event into a thrown exception, which
|
||||
is exactly what this library promises not to do.
|
||||
|
||||
`sessionError` carries two kinds of failure, and `PduRefusedError` is what separates them:
|
||||
|
||||
- **A PDU the peer sent that the codec could not read with the stream still in sync.** The link is
|
||||
healthy and only that one PDU is lost, so this is the kind to count rather than alert on.
|
||||
- **Everything else**: the session or the socket failing, and a hook or listener that threw or, if it
|
||||
was `async`, rejected.
|
||||
|
||||
```javascript
|
||||
import { PduRefusedError } from '@larvit/smpp';
|
||||
|
||||
session.on('sessionError', err => {
|
||||
if (err instanceof PduRefusedError) {
|
||||
log.warn('the peer sent a PDU that could not be read', {
|
||||
cmdName: err.header.cmdName ?? err.header.cmdId,
|
||||
reason: err.reason,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
log.error('the session failed', { message: err.message });
|
||||
});
|
||||
```
|
||||
|
||||
`reason` is `command`, `body` or `tlvs`, naming the part the codec stopped at, and `header` is the 16
|
||||
octets that did parse: `cmdId`, `cmdLength`, `cmdName`, `cmdStatusId` and `seqNr`. `cmdName` is
|
||||
undefined where the command id names no command this library knows — `PduHeader` is its type, for a
|
||||
TypeScript consumer passing it on.
|
||||
|
||||
A refused inbound `deliver_sm` is lost traffic: a message or a receipt that never arrives as `sms` or
|
||||
`dlr`, and this event is where that loss shows up. A refused *response* is reported twice where a
|
||||
call is still waiting on it, once as the `err` that `sendSms()` or `send()` returns and once here.
|
||||
That is deliberate: the call answers what became of that one send, and the event is what shows a peer
|
||||
answering unreadably at all.
|
||||
|
||||
## Logging
|
||||
|
||||
`log` takes any object with `debug`, `error`, `info`, `verbose` and `warn` methods, each
|
||||
@@ -306,7 +341,7 @@ TypeScript users can import `SmppLog` to have the compiler check one.
|
||||
| `close` | The session is over, because nothing will bring the link back. Fires once, whether you closed it or the link failed for good. |
|
||||
| `disconnected` | The link dropped and the reconnect loop will retry it. Do not open a replacement client here — the session you hold comes back on its own, and `reconnected` says when. Fires again for each attempt that reconnects and then fails, so it is not one-to-one with `reconnected`. |
|
||||
| `reconnected` | The client re-bound after a drop. |
|
||||
| `sessionError` | Something failed on a live session, including a hook or listener that threw or, if it was `async`, rejected. Fires for each PDU the codec refused, and the link carries on: a refused request is answered with the status SMPP names, and a refused response is answered with nothing and settles the request it named as `unanswered`. |
|
||||
| `sessionError` | Something failed on a live session, including a hook or listener that threw or, if it was `async`, rejected. Fires for each PDU the codec refused as well, carrying a `PduRefusedError` while the link carries on: a refused request is answered with the status SMPP names, and a refused response is answered with nothing and settles the request it named as `unanswered`. [Errors](#errors) tells the two kinds apart. |
|
||||
| `data` | Raw bytes arrived on the socket. |
|
||||
| `incomingPdu` | A complete PDU arrived, as a buffer. |
|
||||
| `incomingPduObj` | The same PDU, parsed into an object. |
|
||||
|
||||
Reference in New Issue
Block a user