Compare commits

3 Commits
dev ... master

Author SHA1 Message Date
PhatPhuckDave
4a5aa484ba Update 2024-07-22 19:52:53 +02:00
a18ac0355c Add source and desitnation set processors 2023-06-07 13:49:39 +02:00
a1e7d3f885 Add protocolid and ucs2 processors 2023-06-07 13:33:42 +02:00
57 changed files with 4512 additions and 2480 deletions

3
.gitignore vendored
View File

@@ -6,3 +6,6 @@ client_sessions.json
center_sessions.json
dist
main.exe
.run
.vscode
build

4
.prettierrc Normal file
View File

@@ -0,0 +1,4 @@
{
"tabWidth": 4,
"useTabs": true
}

View File

@@ -1,19 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build To Web" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="rsync -auzhvisP ./dist ../smsgw-tester-web/electron" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="tsc" run_configuration_type="js.build_tools.npm" />
</method>
</configuration>
</component>

View File

@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.ts" type="TypeScriptProgramRunner" factoryName="TypeScript">
<module name="smsgwtester" />
<envs>
<env name="LOG_LEVEL" value="0" />
</envs>
<option name="interpreterRef" value="project" />
<option name="enabledTsNodeEsmLoader" value="false" />
<option name="interpreterOptions" value="" />
<option name="workingDirectory" value="C:\Users\Administrator\WebstormProjects\smsgwtester\src" />
<option name="tsconfigFile" value="" />
<option name="extraTypeScriptOptions" value="" />
<option name="scriptName" value="$PROJECT_DIR$/src/main.ts" />
<option name="programParameters" value="" />
<option name="tsnodePackage" value="~\WebstormProjects\smsgwtester\node_modules\ts-node" />
<method v="2" />
</configuration>
</component>

View File

@@ -1,14 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="pkg:windows" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="pkg:windows" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="tsc" run_configuration_type="js.build_tools.npm" />
</method>
</configuration>
</component>

View File

@@ -1,12 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="tsc" type="js.build_tools.npm" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="tsc" />
</scripts>
<node-interpreter value="project" />
<envs />
<method v="2" />
</configuration>
</component>

10
Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM ubuntu:22.04
WORKDIR /app
COPY "./main.exe/" "./"
EXPOSE 6555
EXPOSE 6556
ENTRYPOINT "./main.exe"

View File

@@ -70,10 +70,37 @@ With this preprocessor enabled the messages body is chopped up into segments bas
**With this preprocessor disabled any message whose body exceeds the maximum smpp message size is truncated to size**.
#### Protocol ID (+2/3 Digit Versions) (1.2)
This processor sets the protocol_id field to a value.
The basic one (ProtocolIdProcessor) sets the value to 1, 2Digit version to 16 and 3Digit version to 128.
#### Source/Destination Set Processor
Generates X random numbers that are then used for sending Y messages.
The interface with this processor is somewhat specific: To specify the amount of msisdns generated prepend "arg:X;" to the message body.
Example: message "arg:100;test123" generates 100 msisdns and sends messages with the body of "test123".
The point of this processor is to generate a set of msisdns to use with a greater number of messages.
For example sending 100k messages from 100 msisdns (effectively sending 100 messages per msisdn) to simulate 100 users.
Note, the picked msisdn per message is random (from the generated set).
### Center Preprocessors
None as of 1.0
#### Destination & Source Enumerator (1.2)
Same as the Client version.
#### Long SMS (1.2)
Same as the Client version.
#### Protocol ID (+2/3 Digit Versions) (1.2)
Same as the Client version.
#### Source/Destination Set Processor (1.2)
Same as the Client version.
### Client Postprocessors

View File

@@ -3,21 +3,26 @@
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"tsc": "tsc",
"dev": "tsup && node build/main.cjs",
"pkg:windows": "pkg ./dist/main.js --target node16-windows-x64 --output main.exe",
"pkg:linux": "pkg ./dist/main.js --target node16-linux-x64 --output main.exe"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"devDependencies": {
"@types/node": "^20.4.9",
"body-parser": "^1.20.2",
"compression": "^1.7.4",
"cors": "^2.8.5",
"express": "^4.18.2",
"nanotimer": "^0.3.15",
"smpp": "^0.6.0-rc.4",
"smpp": "0.6.0-rc.4",
"ts-node": "^10.9.1",
"tsup": "^7.2.0",
"typescript": "^5.1.6",
"ws": "^8.13.0",
"zlib": "^1.0.5"
}

1705
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

32
smpp_tester_options.json Normal file
View File

@@ -0,0 +1,32 @@
[
{
"url": "smpp://localhost:4445",
"connection_count": 8,
"system_id": "snt",
"password": "snt",
"source_addr": "44455",
"destination_addr": "test123",
"short_message": "SNT BENCHMARK!",
"message_count": 100
},
{
"url": "smpp://172.17.77.204:4445",
"connection_count": 8,
"system_id": "snt",
"password": "snt",
"source_addr": "44455",
"destination_addr": "test123",
"short_message": "SNT BENCHMARK!",
"message_count": 100
},
{
"url": "smpp://172.17.77.14:4445",
"connection_count": 8,
"system_id": "snt",
"password": "snt",
"source_addr": "44455",
"destination_addr": "test123",
"short_message": "SNT BENCHMARK!",
"message_count": 100
}
]

View File

@@ -14,199 +14,199 @@ const smpp = require("smpp");
const PORT_RELISTEN_DELAY: number = Number(process.env.PORT_RELISTEN_DELAY) || 500;
export default class Center extends SmppSession {
readonly STATUSES: string[] = [
"PORT BUSY",
"WAITING CONNECTION",
"CONNECTING",
"CONNECTED",
"BUSY"
];
_username: string;
_password: string;
_id: number;
_status: string = this.STATUSES[0];
port: number;
readonly STATUSES: string[] = [
"PORT BUSY",
"WAITING CONNECTION",
"CONNECTING",
"CONNECTED",
"BUSY"
];
_username: string;
_password: string;
_id: number;
_status: string = this.STATUSES[0];
port: number;
readonly logger: Logger;
pendingSessions: any[] = [];
sessions: any[] = [];
private nextSession: number = 0;
private server: any;
readonly logger: Logger;
pendingSessions: any[] = [];
sessions: any[] = [];
private nextSession: number = 0;
private server: any;
constructor(id: number, port: number, username: string, password: string) {
super();
this._id = id;
this._username = username;
this._password = password;
this.port = port;
constructor(id: number, port: number, username: string, password: string) {
super();
this._id = id;
this._username = username;
this._password = password;
this.port = port;
this._defaultSingleJob = Job.createEmptySingle('deliver_sm');
this._defaultMultipleJob = Job.createEmptyMultiple('deliver_sm');
this._defaultSingleJob = Job.createEmptySingle('deliver_sm');
this._defaultMultipleJob = Job.createEmptyMultiple('deliver_sm');
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(SubmitSmReplyProcessor.name));
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(BindTranscieverReplyProcessor.name));
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(EnquireLinkReplyProcessor.name));
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(SubmitSmReplyProcessor.name));
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(BindTranscieverReplyProcessor.name));
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(EnquireLinkReplyProcessor.name));
this.logger = new Logger(`Center-${id}`);
this.logger = new Logger(`Center-${id}`);
this.initialize();
}
this.initialize();
}
_defaultSingleJob: Job;
_defaultSingleJob: Job;
get defaultSingleJob(): Job {
return this._defaultSingleJob;
}
get defaultSingleJob(): Job {
return this._defaultSingleJob;
}
set defaultSingleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'deliver_sm';
}
super.defaultSingleJob = job;
}
set defaultSingleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'deliver_sm';
}
super.defaultSingleJob = job;
}
_defaultMultipleJob: Job;
_defaultMultipleJob: Job;
get defaultMultipleJob(): Job {
return this._defaultMultipleJob;
}
get defaultMultipleJob(): Job {
return this._defaultMultipleJob;
}
set defaultMultipleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'deliver_sm';
}
super.defaultMultipleJob = job;
}
set defaultMultipleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'deliver_sm';
}
super.defaultMultipleJob = job;
}
sendPdu(pdu: PDU, force?: boolean): Promise<object> {
return new Promise((resolve, reject) => {
if (!force) {
this.validateSessions(reject);
}
this.logger.log5(`Center-${this.id} sending PDU: ${JSON.stringify(pdu)}`);
let pduCopy = new smpp.PDU(pdu.command, {...pdu});
let session = this.getNextSession();
this.processors.Preprocessor.forEach((processor: PduProcessor) => processor.processPdu(session, pduCopy, this));
this.doSendPdu(pduCopy, session).then((replyPdu: any) => {
resolve(replyPdu);
});
});
}
sendPdu(pdu: PDU, force?: boolean): Promise<object> {
return new Promise((resolve, reject) => {
if (!force) {
this.validateSessions(reject);
}
let pduCopy = new smpp.PDU(pdu.command, {...pdu});
let session = this.getNextSession();
this.processors.Preprocessor.forEach((processor: PduProcessor) => processor.processPdu(session, pduCopy, this));
this.logger.log5(`Center-${this.id} sending PDU: ${JSON.stringify(pduCopy)}`);
this.doSendPdu(pduCopy, session).then((replyPdu: any) => {
resolve(replyPdu);
});
});
}
sendMultiple(job: Job): Promise<void> {
return new Promise((resolve, reject) => {
this.validateSessions(reject);
if (!job.count || !job.perSecond) {
reject(`Center-${this.id} sendMultiple failed: invalid job, missing fields`);
}
this.logger.log1(`Center-${this.id} sending multiple messages: ${JSON.stringify(job)}`);
sendMultiple(job: Job): Promise<void> {
return new Promise((resolve, reject) => {
this.validateSessions(reject);
if (!job.count || !job.perSecond) {
reject(`Center-${this.id} sendMultiple failed: invalid job, missing fields`);
}
this.logger.log1(`Center-${this.id} sending multiple messages: ${JSON.stringify(job)}`);
let counter = 0;
let previousUpdateCounter = 0;
let counter = 0;
let previousUpdateCounter = 0;
this.counterUpdateTimer.setInterval(() => {
if (previousUpdateCounter !== counter) {
this.eventEmitter.emit(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter);
previousUpdateCounter = counter;
}
}, '', `${this.MESSAGE_SEND_UPDATE_DELAY / 1000} s`);
this.counterUpdateTimer.setInterval(() => {
if (previousUpdateCounter !== counter) {
this.eventEmitter.emit(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter);
previousUpdateCounter = counter;
}
}, '', `${this.MESSAGE_SEND_UPDATE_DELAY / 1000} s`);
let count = job.count || 1;
let interval = 1 / (job.perSecond || 1);
this.setStatus(4);
this.sendTimer.setInterval(() => {
if (count > 0 && counter >= count) {
this.cancelSendInterval();
} else {
this.sendPdu(job.pdu, true);
counter++;
}
}, '', `${interval} s`);
resolve();
});
}
let count = job.count || 1;
let interval = 1 / (job.perSecond || 1);
this.setStatus(4);
this.sendTimer.setInterval(() => {
if (count > 0 && counter >= count) {
this.cancelSendInterval();
} else {
this.sendPdu(job.pdu, true);
counter++;
}
}, '', `${interval} s`);
resolve();
});
}
initialize(): void {
this.server = smpp.createServer({}, this.eventSessionConnected.bind(this));
this.server.on('error', this.eventServerError.bind(this));
this.doListen();
this.setStatus(1);
}
initialize(): void {
this.server = smpp.createServer({}, this.eventSessionConnected.bind(this));
this.server.on('error', this.eventServerError.bind(this));
this.doListen();
this.setStatus(1);
}
close(): Promise<void> {
return new Promise((resolve, reject) => {
this.logger.log1(`Center-${this.id} closing active connections`);
this.sessions.forEach((session: any) => {
session.close();
});
this.setStatus(1);
resolve();
});
}
close(): Promise<void> {
return new Promise((resolve, reject) => {
this.logger.log1(`Center-${this.id} closing active connections`);
this.sessions.forEach((session: any) => {
session.close();
});
this.setStatus(1);
resolve();
});
}
destroy(): void {
this.server.close();
}
destroy(): void {
this.server.close();
}
postSerialize(obj: any): object {
obj.port = this.port;
return obj;
}
postSerialize(obj: any): object {
obj.port = this.port;
return obj;
}
updateStatus(): void {
if (this.sessions.length > 0) {
this.setStatus(3);
} else if (this.pendingSessions.length > 0) {
this.setStatus(2);
} else {
this.setStatus(1);
}
}
updateStatus(): void {
if (this.sessions.length > 0) {
this.setStatus(3);
} else if (this.pendingSessions.length > 0) {
this.setStatus(2);
} else {
this.setStatus(1);
}
}
private doListen(): void {
this.server.listen(this.port);
}
private doListen(): void {
this.server.listen(this.port);
}
private validateSessions(reject: (reason?: any) => void) {
if (this.sessions.length === 0) {
reject(`No clients connected`);
}
}
private validateSessions(reject: (reason?: any) => void) {
if (this.sessions.length === 0) {
reject(`No clients connected`);
}
}
private getNextSession(): any {
if (this.sessions.length === 0) {
return null;
}
let session = this.sessions[this.nextSession];
this.nextSession = (this.nextSession + 1) % this.sessions.length;
return session;
}
private getNextSession(): any {
if (this.sessions.length === 0) {
return null;
}
let session = this.sessions[this.nextSession];
this.nextSession = (this.nextSession + 1) % this.sessions.length;
return session;
}
private eventSessionConnected(session: any): void {
this.logger.log1(`A client connected to center-${this.id}`);
this.pendingSessions.push(session);
session.on('close', this.eventSessionClose.bind(this, session));
session.on('error', this.eventSessionError.bind(this, session));
session.on('pdu', this.eventAnyPdu.bind(this, session));
this.updateStatus();
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
private eventSessionConnected(session: any): void {
this.logger.log1(`A client connected to center-${this.id}`);
this.pendingSessions.push(session);
session.on('close', this.eventSessionClose.bind(this, session));
session.on('error', this.eventSessionError.bind(this, session));
session.on('pdu', this.eventAnyPdu.bind(this, session));
this.updateStatus();
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
private eventSessionError(session: any): void {
this.logger.log1(`A client encountered an error on center-${this.id}`);
}
private eventSessionError(session: any): void {
this.logger.log1(`A client encountered an error on center-${this.id}`);
}
private eventServerError(): void {
this.logger.log1(`Center tried listening on port which is already in use, retrying in ${PORT_RELISTEN_DELAY}`);
this.setStatus(0);
setTimeout(this.doListen.bind(this), PORT_RELISTEN_DELAY);
}
private eventServerError(): void {
this.logger.log1(`Center tried listening on port which is already in use, retrying in ${PORT_RELISTEN_DELAY}`);
this.setStatus(0);
setTimeout(this.doListen.bind(this), PORT_RELISTEN_DELAY);
}
private eventSessionClose(session: any): void {
this.logger.log1(`A client disconnected from center-${this.id}`);
this.sessions = this.sessions.filter((s: any) => s !== session);
this.nextSession = 0;
this.pendingSessions = this.pendingSessions.filter((s: any) => s !== session);
this.updateStatus();
}
private eventSessionClose(session: any): void {
this.logger.log1(`A client disconnected from center-${this.id}`);
this.sessions = this.sessions.filter((s: any) => s !== session);
this.nextSession = 0;
this.pendingSessions = this.pendingSessions.filter((s: any) => s !== session);
this.updateStatus();
}
}

View File

@@ -6,15 +6,15 @@ import Center from "./Center";
const CENTER_SESSIONS_FILE: string = process.env.CENTER_SESSIONS_FILE || "center_sessions.json";
export default class CenterSessionManager extends SessionManager {
StorageFile: string = CENTER_SESSIONS_FILE
ManagedSessionClass: typeof Center = Center;
identifier: string = "Center";
readonly logger: Logger = new Logger("CenterSessionManager");
StorageFile: string = CENTER_SESSIONS_FILE
ManagedSessionClass: typeof Center = Center;
identifier: string = "Center";
readonly logger: Logger = new Logger("CenterSessionManager");
constructor() {
super();
this.setup();
}
constructor() {
super();
this.setup();
}
comparatorFn: (arg: any, session: SmppSession) => boolean = (arg: any, session: SmppSession) => (session as Center).port === arg;
comparatorFn: (arg: any, session: SmppSession) => boolean = (arg: any, session: SmppSession) => (session as Center).port === arg;
}

View File

@@ -13,271 +13,271 @@ const smpp = require("smpp");
const AUTO_ENQUIRE_LINK_PERIOD: number = Number(process.env.AUTO_ENQUIRE_LINK_PERIOD) || 30000;
export default class Client extends SmppSession {
readonly STATUSES: string[] = [
"NOT CONNECTED",
"CONNECTING",
"CONNECTED",
"BINDING",
"BOUND",
"BUSY",
]
url: string;
_username: string;
_password: string;
_id: number;
_status: string = this.STATUSES[0];
readonly logger: Logger;
private session?: any;
private connectPromise: PersistentPromise | null = null;
private bindPromise: PersistentPromise | null = null;
private closePromise: PersistentPromise | null = null;
readonly STATUSES: string[] = [
"NOT CONNECTED",
"CONNECTING",
"CONNECTED",
"BINDING",
"BOUND",
"BUSY",
]
url: string;
_username: string;
_password: string;
_id: number;
_status: string = this.STATUSES[0];
readonly logger: Logger;
private session?: any;
private connectPromise: PersistentPromise | null = null;
private bindPromise: PersistentPromise | null = null;
private closePromise: PersistentPromise | null = null;
constructor(id: number, url: string, username: string, password: string) {
super();
this._id = id;
this._username = username;
this._password = password;
this.url = url;
constructor(id: number, url: string, username: string, password: string) {
super();
this._id = id;
this._username = username;
this._password = password;
this.url = url;
this._defaultSingleJob = Job.createEmptySingle('submit_sm');
this._defaultMultipleJob = Job.createEmptyMultiple('submit_sm');
this._defaultSingleJob = Job.createEmptySingle('submit_sm');
this._defaultMultipleJob = Job.createEmptyMultiple('submit_sm');
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(DeliverSmReplyProcessor.name));
ProcessorManager.attachProcessors(this, ProcessorManager.getProcessors(DeliverSmReplyProcessor.name));
this.logger = new Logger(`Client-${id}`);
}
this.logger = new Logger(`Client-${id}`);
}
_defaultSingleJob: Job;
_defaultSingleJob: Job;
get defaultSingleJob(): Job {
return this._defaultSingleJob;
}
get defaultSingleJob(): Job {
return this._defaultSingleJob;
}
set defaultSingleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'submit_sm';
}
super.defaultSingleJob = job;
}
set defaultSingleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'submit_sm';
}
super.defaultSingleJob = job;
}
_defaultMultipleJob: Job;
_defaultMultipleJob: Job;
get defaultMultipleJob(): Job {
return this._defaultMultipleJob;
}
get defaultMultipleJob(): Job {
return this._defaultMultipleJob;
}
set defaultMultipleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'submit_sm';
}
super.defaultMultipleJob = job;
}
set defaultMultipleJob(job: Job) {
if (job.pdu && !job.pdu.command) {
job.pdu.command = 'submit_sm';
}
super.defaultMultipleJob = job;
}
destroy(): void {
}
destroy(): void {
}
doConnect(): PersistentPromise {
this.connectPromise = new PersistentPromise((resolve, reject) => {
if (this.status !== this.STATUSES[0]) {
let errorString = `Client-${this.id} already connected`;
this.logger.log1(errorString);
reject(errorString);
return;
}
doConnect(): PersistentPromise {
this.connectPromise = new PersistentPromise((resolve, reject) => {
if (this.status !== this.STATUSES[0]) {
let errorString = `Client-${this.id} already connected`;
this.logger.log1(errorString);
reject(errorString);
return;
}
this.logger.log1(`Client-${this.id} connecting to ${this.url}`);
this.setStatus(1);
this.connectSession().then(resolve, ((err: any) => {
this.logger.log1(`Client-${this.id} connection failed: ${err}`);
this.setStatus(0);
this.session.close();
reject(err);
}));
});
return this.connectPromise;
}
this.logger.log1(`Client-${this.id} connecting to ${this.url}`);
this.setStatus(1);
this.connectSession().then(resolve, ((err: any) => {
this.logger.log1(`Client-${this.id} connection failed: ${err}`);
this.setStatus(0);
this.session.close();
reject(err);
}));
});
return this.connectPromise;
}
doBind(): PersistentPromise {
this.bindPromise = new PersistentPromise((resolve, reject) => {
this.validateFields(reject);
doBind(): PersistentPromise {
this.bindPromise = new PersistentPromise((resolve, reject) => {
this.validateFields(reject);
this.session.bind_transceiver({
system_id: this.username,
password: this.password,
}, this.eventBindReply.bind(this));
this.setStatus(3);
});
return this.bindPromise;
}
this.session.bind_transceiver({
system_id: this.username,
password: this.password,
}, this.eventBindReply.bind(this));
this.setStatus(3);
});
return this.bindPromise;
}
connectAndBind(): Promise<void> {
return this.doConnect().then(this.doBind.bind(this), (error) => {
this.logger.log1(`Client-${this.id} connectAndBind failed: ${error}`);
});
}
connectAndBind(): Promise<void> {
return this.doConnect().then(this.doBind.bind(this), (error) => {
this.logger.log1(`Client-${this.id} connectAndBind failed: ${error}`);
});
}
postSerialize(obj: any): object {
obj.url = this.url;
return obj;
}
postSerialize(obj: any): object {
obj.url = this.url;
return obj;
}
close(): Promise<void> {
this.logger.log1(`Client-${this.id} closing connection`);
if (this.session) {
return Promise.resolve(this.session.close());
} else {
return Promise.resolve();
}
}
close(): Promise<void> {
this.logger.log1(`Client-${this.id} closing connection`);
if (this.session) {
return Promise.resolve(this.session.close());
} else {
return Promise.resolve();
}
}
sendPdu(pdu: PDU, force?: boolean): Promise<object> {
return new Promise((resolve, reject) => {
if (!force) {
this.validateSession(reject);
this.validateBound(reject);
}
// Is this expensive...?
let pduCopy = new smpp.PDU(pdu.command, {...pdu});
this.processors.Preprocessor.forEach((processor: PduProcessor) => processor.processPdu(this.session, pduCopy, this));
this.logger.log5(`Client-${this.id} sending PDU: ${JSON.stringify(pduCopy)}`);
this.doSendPdu(pduCopy, this.session).then((replyPdu: any) => {
resolve(replyPdu);
});
});
}
sendPdu(pdu: PDU, force?: boolean): Promise<object> {
return new Promise((resolve, reject) => {
if (!force) {
this.validateSession(reject);
this.validateBound(reject);
}
// Is this expensive...?
let pduCopy = new smpp.PDU(pdu.command, {...pdu});
this.processors.Preprocessor.forEach((processor: PduProcessor) => processor.processPdu(this.session, pduCopy, this));
this.logger.log5(`Client-${this.id} sending PDU: ${JSON.stringify(pduCopy)}`);
this.doSendPdu(pduCopy, this.session).then((replyPdu: any) => {
resolve(replyPdu);
});
});
}
sendMultiple(job: Job): Promise<void> {
return new Promise((resolve, reject) => {
this.validateSession(reject);
this.validateBound(reject);
if (!job.count || !job.perSecond) {
reject(`Client-${this.id} sendMultiple failed: invalid job, missing fields`);
}
this.logger.log1(`Client-${this.id} sending multiple messages: ${JSON.stringify(job)}`);
sendMultiple(job: Job): Promise<void> {
return new Promise((resolve, reject) => {
this.validateSession(reject);
this.validateBound(reject);
if (!job.count || !job.perSecond) {
reject(`Client-${this.id} sendMultiple failed: invalid job, missing fields`);
}
this.logger.log1(`Client-${this.id} sending multiple messages: ${JSON.stringify(job)}`);
this.setStatus(4);
this.setStatus(4);
let counter: number = 0;
let previousUpdateCounter: number = 0;
let counter: number = 0;
let previousUpdateCounter: number = 0;
this.counterUpdateTimer.setInterval(() => {
if (previousUpdateCounter !== counter) {
this.eventEmitter.emit(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter);
previousUpdateCounter = counter;
}
}, '', `${this.MESSAGE_SEND_UPDATE_DELAY / 1000} s`);
this.counterUpdateTimer.setInterval(() => {
if (previousUpdateCounter !== counter) {
this.eventEmitter.emit(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, counter);
previousUpdateCounter = counter;
}
}, '', `${this.MESSAGE_SEND_UPDATE_DELAY / 1000} s`);
let count: number = job.count || 1;
let interval: number = 1 / (job.perSecond || 1);
this.setStatus(5);
this.sendTimer.setInterval(() => {
if (count > 0 && counter >= count) {
this.cancelSendInterval();
} else {
this.sendPdu(job.pdu, true)
.catch(e => this.logger.log1(`Error sending message: ${e}`));
counter++;
}
}, '', `${interval} s`);
resolve();
});
}
let count: number = job.count || 1;
let interval: number = 1 / (job.perSecond || 1);
this.setStatus(5);
this.sendTimer.setInterval(() => {
if (count > 0 && counter >= count) {
this.cancelSendInterval();
} else {
this.sendPdu(job.pdu, true)
.catch(e => this.logger.log1(`Error sending message: ${e}`));
counter++;
}
}, '', `${interval} s`);
resolve();
});
}
private connectSession(): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.validateFields(reject);
this.logger.log1(`Client-${this.id} connecting to ${this.url}`);
private connectSession(): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.validateFields(reject);
this.logger.log1(`Client-${this.id} connecting to ${this.url}`);
this.session = smpp.connect({
url: this.url, auto_enquire_link_period: AUTO_ENQUIRE_LINK_PERIOD,
}, this.eventSessionConnected.bind(this));
this.session.on('error', this.eventSessionError.bind(this));
this.session.on('close', this.eventSessionClose.bind(this));
this.session.on('pdu', this.eventAnyPdu.bind(this, this.session));
});
}
this.session = smpp.connect({
url: this.url, auto_enquire_link_period: AUTO_ENQUIRE_LINK_PERIOD,
}, this.eventSessionConnected.bind(this));
this.session.on('error', this.eventSessionError.bind(this));
this.session.on('close', this.eventSessionClose.bind(this));
this.session.on('pdu', this.eventAnyPdu.bind(this, this.session));
});
}
private eventSessionConnected(): void {
this.logger.log1(`Client-${this.id} connected to ${this.url}`);
this.setStatus(2);
if (this.connectPromise) {
this.connectPromise.resolve();
} else {
this.logger.log1(`Client-${this.id} connected without connect promise`);
}
}
private eventSessionConnected(): void {
this.logger.log1(`Client-${this.id} connected to ${this.url}`);
this.setStatus(2);
if (this.connectPromise) {
this.connectPromise.resolve();
} else {
this.logger.log1(`Client-${this.id} connected without connect promise`);
}
}
private eventSessionError(pdu: any): void {
this.logger.log1(`Client-${this.id} error on ${this.url} - ${pdu.message}`);
this.setStatus(0);
this.rejectPromises();
}
private eventSessionError(pdu: any): void {
this.logger.log1(`Client-${this.id} error on ${this.url} - ${pdu.message}`);
this.setStatus(0);
this.rejectPromises();
}
private eventSessionClose(): void {
this.logger.log1(`Client-${this.id} closed on ${this.url}`);
this.setStatus(0);
this.rejectPromises();
}
private eventSessionClose(): void {
this.logger.log1(`Client-${this.id} closed on ${this.url}`);
this.setStatus(0);
this.rejectPromises();
}
private eventBindReply(pdu: PDU): void {
if (pdu.command_status === 0) {
this.logger.log1(`Client-${this.id} bound to ${this.url}`);
this.setStatus(4);
if (this.bindPromise) {
this.bindPromise.resolve();
}
} else {
this.logger.log1(`Client-${this.id} bind failed to ${this.url}`);
this.setStatus(2);
if (this.bindPromise) {
this.bindPromise.reject(pdu);
}
}
}
private eventBindReply(pdu: PDU): void {
if (pdu.command_status === 0) {
this.logger.log1(`Client-${this.id} bound to ${this.url}`);
this.setStatus(4);
if (this.bindPromise) {
this.bindPromise.resolve();
}
} else {
this.logger.log1(`Client-${this.id} bind failed to ${this.url}`);
this.setStatus(2);
if (this.bindPromise) {
this.bindPromise.reject(pdu);
}
}
}
private rejectPromises(err?: any): void {
if (this.connectPromise) {
this.connectPromise.reject(err);
}
if (this.bindPromise) {
this.bindPromise.reject(err);
}
if (this.closePromise) {
this.closePromise.resolve();
}
}
private rejectPromises(err?: any): void {
if (this.connectPromise) {
this.connectPromise.reject(err);
}
if (this.bindPromise) {
this.bindPromise.reject(err);
}
if (this.closePromise) {
this.closePromise.resolve();
}
}
private validateFields(reject: (reason?: any) => void) {
if (!this.url) {
let error = `Client-${this.id} has no url set`;
this.logger.log1(error);
reject(error);
}
if (!this.username) {
let error = `Client-${this.id} has no username set`;
this.logger.log1(error);
reject(error);
}
if (!this.password) {
let error = `Client-${this.id} has no password set`;
this.logger.log1(error);
reject(error);
}
}
private validateFields(reject: (reason?: any) => void) {
if (!this.url) {
let error = `Client-${this.id} has no url set`;
this.logger.log1(error);
reject(error);
}
if (!this.username) {
let error = `Client-${this.id} has no username set`;
this.logger.log1(error);
reject(error);
}
if (!this.password) {
let error = `Client-${this.id} has no password set`;
this.logger.log1(error);
reject(error);
}
}
private validateSession(reject: (reason?: any) => void) {
if (!this.session) {
let errorMessage = `Client-${this.id} session is not defined`;
this.logger.log1(errorMessage);
reject(errorMessage);
}
}
private validateSession(reject: (reason?: any) => void) {
if (!this.session) {
let errorMessage = `Client-${this.id} session is not defined`;
this.logger.log1(errorMessage);
reject(errorMessage);
}
}
private validateBound(reject: (reason?: any) => void) {
if (this.status !== this.STATUSES[4]) {
let errorMessage = `Client-${this.id} is not bound`;
this.logger.log1(errorMessage);
reject(errorMessage);
}
}
private validateBound(reject: (reason?: any) => void) {
if (this.status !== this.STATUSES[4]) {
let errorMessage = `Client-${this.id} is not bound`;
this.logger.log1(errorMessage);
reject(errorMessage);
}
}
}

View File

@@ -6,15 +6,15 @@ import Client from "./Client";
const CLIENT_SESSIONS_FILE: string = process.env.CLIENT_SESSIONS_FILE || "client_sessions.json";
export default class ClientSessionManager extends SessionManager {
StorageFile: string = CLIENT_SESSIONS_FILE;
ManagedSessionClass: typeof Client = Client;
identifier: string = "Client";
readonly logger: Logger = new Logger("ClientSessionManager");
StorageFile: string = CLIENT_SESSIONS_FILE;
ManagedSessionClass: typeof Client = Client;
identifier: string = "Client";
readonly logger: Logger = new Logger("ClientSessionManager");
constructor() {
super();
this.setup();
}
constructor() {
super();
this.setup();
}
comparatorFn: (arg: any, session: SmppSession) => boolean = (arg: any, session: SmppSession) => (session as Client).url === arg;
comparatorFn: (arg: any, session: SmppSession) => boolean = (arg: any, session: SmppSession) => (session as Client).url === arg;
}

View File

@@ -1,37 +1,37 @@
export type PDU = {
command?: string;
command_id?: number;
command_length?: number;
command_status?: number;
data_coding?: number;
dest_addr_npi?: number;
dest_addr_ton?: number;
destination_addr?: string;
esm_class?: number,
password?: string,
priority_flag?: number,
protocol_id?: number,
registered_delivery?: number,
replace_if_present_flag?: number,
response?: (...args: any[]) => PDU,
schedule_delivery_time?: string,
sequence_number?: number,
service_type?: string,
short_message?: any,
sm_default_msg_id?: number,
source_addr?: string,
source_addr_npi?: number,
source_addr_ton?: number,
system_id?: string,
validity_period?: string
command?: string;
command_id?: number;
command_length?: number;
command_status?: number;
data_coding?: number;
dest_addr_npi?: number;
dest_addr_ton?: number;
destination_addr?: string;
esm_class?: number,
password?: string,
priority_flag?: number,
protocol_id?: number,
registered_delivery?: number,
replace_if_present_flag?: number,
response?: (...args: any[]) => PDU,
schedule_delivery_time?: string,
sequence_number?: number,
service_type?: string,
short_message?: any,
sm_default_msg_id?: number,
source_addr?: string,
source_addr_npi?: number,
source_addr_ton?: number,
system_id?: string,
validity_period?: string
};
export type SerializedJob = {
pdu: PDU;
count?: number;
perSecond?: number;
pdu: PDU;
count?: number;
perSecond?: number;
};
export type WSMessage = {
type: string;
identifier: string;
data?: any;
type: string;
identifier: string;
data?: any;
};

View File

@@ -8,58 +8,58 @@ import SmppSession from "../SmppSession";
import RequestHandler from "./RequestHandler";
export default class CenterRequestHandler extends RequestHandler {
sessionManager: CenterSessionManager;
logger: Logger = new Logger(this.constructor.name);
sessionManager: CenterSessionManager;
logger: Logger = new Logger(this.constructor.name);
constructor(sessionManager: SessionManager) {
super();
this.sessionManager = sessionManager as CenterSessionManager;
}
constructor(sessionManager: SessionManager) {
super();
this.sessionManager = sessionManager as CenterSessionManager;
}
doGetAvailableProcessors(req: any, res: any): void {
this.logger.log1("Getting available processors");
let processors: PduProcessor[] = ProcessorManager.getProcessorsForType(Center.name);
res.send(processors.map((processor: any) => processor.serialize()));
}
doGetAvailableProcessors(req: any, res: any): void {
this.logger.log1("Getting available processors");
let processors: PduProcessor[] = ProcessorManager.getProcessorsForType(Center.name);
res.send(processors.map((processor: any) => processor.serialize()));
}
doGetAppliedProcessors(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = session.appliedProcessors;
res.send(processors.map((processor: any) => processor.serialize()));
}, this.handleSessionNotFound.bind(this, req, res));
}
doGetAppliedProcessors(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = session.appliedProcessors;
res.send(processors.map((processor: any) => processor.serialize()));
}, this.handleSessionNotFound.bind(this, req, res));
}
doAddProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.attachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doAddProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.attachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doRemoveProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.detachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doRemoveProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.detachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doPost(req: any, res: any): void {
this.logger.log1("Creating center session");
this.sessionManager.createSession(req.body.port, req.body.username, req.body.password).then((session: SmppSession) => {
res.send(session.serialize());
}, (err: any) => {
this.logger.log1(`Failed to create center session: ${err}`);
res.status(500).send();
});
}
doPost(req: any, res: any): void {
this.logger.log1("Creating center session");
this.sessionManager.createSession(req.body.port, req.body.username, req.body.password).then((session: SmppSession) => {
res.send(session.serialize());
}, (err: any) => {
this.logger.log1(`Failed to create center session: ${err}`);
res.status(500).send();
});
}
doConnect(req: any, res: any): void {
throw new Error("Method not implemented.");
}
doConnect(req: any, res: any): void {
throw new Error("Method not implemented.");
}
doBind(req: any, res: any): void {
throw new Error("Method not implemented.");
}
doBind(req: any, res: any): void {
throw new Error("Method not implemented.");
}
}

View File

@@ -8,76 +8,76 @@ import SmppSession from "../SmppSession";
import RequestHandler from "./RequestHandler";
export default class ClientRequestHandler extends RequestHandler {
sessionManager: ClientSessionManager;
logger: Logger = new Logger(this.constructor.name);
sessionManager: ClientSessionManager;
logger: Logger = new Logger(this.constructor.name);
constructor(sessionManager: SessionManager) {
super();
this.sessionManager = sessionManager as ClientSessionManager;
}
constructor(sessionManager: SessionManager) {
super();
this.sessionManager = sessionManager as ClientSessionManager;
}
doGetAvailableProcessors(req: any, res: any): void {
this.logger.log1("Getting available processors");
let processors: PduProcessor[] = ProcessorManager.getProcessorsForType(Client.name);
res.send(processors.map((processor: any) => processor.serialize()));
}
doGetAvailableProcessors(req: any, res: any): void {
this.logger.log1("Getting available processors");
let processors: PduProcessor[] = ProcessorManager.getProcessorsForType(Client.name);
res.send(processors.map((processor: any) => processor.serialize()));
}
doGetAppliedProcessors(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = session.appliedProcessors;
res.send(processors.map((processor: any) => processor.serialize()));
}, this.handleSessionNotFound.bind(this, req, res));
}
doGetAppliedProcessors(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = session.appliedProcessors;
res.send(processors.map((processor: any) => processor.serialize()));
}, this.handleSessionNotFound.bind(this, req, res));
}
doAddProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.attachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doAddProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.attachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doRemoveProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.detachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doRemoveProcessor(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let processors: PduProcessor[] = ProcessorManager.getProcessors(req.body.name);
ProcessorManager.detachProcessors(session, processors);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doPost(req: any, res: any): void {
this.logger.log1("Creating client session");
this.sessionManager.createSession(req.body.url, req.body.username, req.body.password).then((session: SmppSession) => {
res.send(session.serialize());
}, (err: any) => {
this.logger.log1(`Failed to create client session: ${err}`);
res.status(500).send();
});
}
doPost(req: any, res: any): void {
this.logger.log1("Creating client session");
this.sessionManager.createSession(req.body.url, req.body.username, req.body.password).then((session: SmppSession) => {
res.send(session.serialize());
}, (err: any) => {
this.logger.log1(`Failed to create client session: ${err}`);
res.status(500).send();
});
}
doBind(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Binding client session with ID ${req.params.id}`)
let client = session as Client;
client.doBind()
.then(() => res.send(session.serialize()))
.catch(err => res.status(400).send({
err: true,
msg: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doBind(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Binding client session with ID ${req.params.id}`)
let client = session as Client;
client.doBind()
.then(() => res.send(session.serialize()))
.catch(err => res.status(400).send({
err: true,
msg: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doConnect(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Connecting client session with ID ${req.params.id}`);
let client = session as Client;
client.doConnect()
.then(() => res.send(session.serialize()))
.catch(err => res.status(400).send({
err: true,
msg: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doConnect(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Connecting client session with ID ${req.params.id}`);
let client = session as Client;
client.doConnect()
.then(() => res.send(session.serialize()))
.catch(err => res.status(400).send({
err: true,
msg: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
}

View File

@@ -8,79 +8,81 @@ const express = require("express");
const bodyParser = require("body-parser");
const compression = require("compression");
const zlib = require("zlib");
const cors = require("cors");
const SERVER_PORT: number = Number(process.env.SERVER_PORT) || 8190;
export default class HttpServer {
private readonly clientRequestHandler: RequestHandler;
private readonly centerRequestHandler: RequestHandler;
private readonly clientRequestHandler: RequestHandler;
private readonly centerRequestHandler: RequestHandler;
private app: any;
private server: any;
private readonly logger: Logger = new Logger(this.constructor.name);
private app: any;
private server: any;
private readonly logger: Logger = new Logger(this.constructor.name);
constructor(clientManager: SessionManager, centerManager: SessionManager) {
this.clientRequestHandler = new ClientRequestHandler(clientManager);
this.centerRequestHandler = new CenterRequestHandler(centerManager);
constructor(clientManager: SessionManager, centerManager: SessionManager) {
this.clientRequestHandler = new ClientRequestHandler(clientManager);
this.centerRequestHandler = new CenterRequestHandler(centerManager);
this.app = express();
this.app.use(bodyParser.json());
this.app = express();
this.app.use(cors());
this.app.use(bodyParser.json());
this.app.use(compression({
level: 9,
strategy: zlib.constants.BROTLI_MODE_TEXT,
}));
this.app.use(compression({
level: 9,
strategy: zlib.constants.BROTLI_MODE_TEXT,
}));
let clientApiPath: string = 'ClientEntity';
let centerApiPath: string = 'CenterEntity';
let clientApiPath: string = 'ClientEntity';
let centerApiPath: string = 'CenterEntity';
this.app.get(`/api/${clientApiPath}`, this.clientRequestHandler.doGet.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}`, this.clientRequestHandler.doPost.bind(this.clientRequestHandler));
this.app.put(`/api/${clientApiPath}/:id/send`, this.clientRequestHandler.doConfigureSingleJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/send/default`, this.clientRequestHandler.doSendSingleJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/send`, this.clientRequestHandler.doSend.bind(this.clientRequestHandler));
this.app.put(`/api/${clientApiPath}/:id/sendMany`, this.clientRequestHandler.doConfigureManyJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/sendMany/default`, this.clientRequestHandler.doSendManyJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/sendMany`, this.clientRequestHandler.doSendMany.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id/sendMany`, this.clientRequestHandler.doCancelSendMany.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/bind`, this.clientRequestHandler.doBind.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/connect`, this.clientRequestHandler.doConnect.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id/connect`, this.clientRequestHandler.doDisconnect.bind(this.clientRequestHandler));
this.app.get(`/api/${clientApiPath}/processors`, this.clientRequestHandler.doGetAvailableProcessors.bind(this.clientRequestHandler));
this.app.get(`/api/${clientApiPath}/:id/processors`, this.clientRequestHandler.doGetAppliedProcessors.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/processors`, this.clientRequestHandler.doAddProcessor.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id/processors`, this.clientRequestHandler.doRemoveProcessor.bind(this.clientRequestHandler));
this.app.get(`/api/${clientApiPath}`, this.clientRequestHandler.doGet.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}`, this.clientRequestHandler.doPost.bind(this.clientRequestHandler));
this.app.put(`/api/${clientApiPath}/:id/send`, this.clientRequestHandler.doConfigureSingleJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/send/default`, this.clientRequestHandler.doSendSingleJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/send`, this.clientRequestHandler.doSend.bind(this.clientRequestHandler));
this.app.put(`/api/${clientApiPath}/:id/sendMany`, this.clientRequestHandler.doConfigureManyJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/sendMany/default`, this.clientRequestHandler.doSendManyJob.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/sendMany`, this.clientRequestHandler.doSendMany.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id/sendMany`, this.clientRequestHandler.doCancelSendMany.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/bind`, this.clientRequestHandler.doBind.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/connect`, this.clientRequestHandler.doConnect.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id/connect`, this.clientRequestHandler.doDisconnect.bind(this.clientRequestHandler));
this.app.get(`/api/${clientApiPath}/processors`, this.clientRequestHandler.doGetAvailableProcessors.bind(this.clientRequestHandler));
this.app.get(`/api/${clientApiPath}/:id/processors`, this.clientRequestHandler.doGetAppliedProcessors.bind(this.clientRequestHandler));
this.app.post(`/api/${clientApiPath}/:id/processors`, this.clientRequestHandler.doAddProcessor.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id/processors`, this.clientRequestHandler.doRemoveProcessor.bind(this.clientRequestHandler));
this.app.get(`/api/${clientApiPath}/:id`, this.clientRequestHandler.doGetById.bind(this.clientRequestHandler));
this.app.patch(`/api/${clientApiPath}/:id`, this.clientRequestHandler.doPatch.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id`, this.clientRequestHandler.doDelete.bind(this.clientRequestHandler));
this.app.get(`/api/${clientApiPath}/:id`, this.clientRequestHandler.doGetById.bind(this.clientRequestHandler));
this.app.patch(`/api/${clientApiPath}/:id`, this.clientRequestHandler.doPatch.bind(this.clientRequestHandler));
this.app.delete(`/api/${clientApiPath}/:id`, this.clientRequestHandler.doDelete.bind(this.clientRequestHandler));
this.app.get(`/api/${centerApiPath}`, this.centerRequestHandler.doGet.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}`, this.centerRequestHandler.doPost.bind(this.centerRequestHandler));
this.app.put(`/api/${centerApiPath}/:id/send`, this.centerRequestHandler.doConfigureSingleJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/send/default`, this.centerRequestHandler.doSendSingleJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/send`, this.centerRequestHandler.doSend.bind(this.centerRequestHandler));
this.app.put(`/api/${centerApiPath}/:id/sendMany`, this.centerRequestHandler.doConfigureManyJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/sendMany/default`, this.centerRequestHandler.doSendManyJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/sendMany`, this.centerRequestHandler.doSendMany.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id/sendMany`, this.centerRequestHandler.doCancelSendMany.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id/connect`, this.centerRequestHandler.doDisconnect.bind(this.centerRequestHandler));
this.app.get(`/api/${centerApiPath}/processors`, this.centerRequestHandler.doGetAvailableProcessors.bind(this.centerRequestHandler));
this.app.get(`/api/${centerApiPath}/:id/processors`, this.centerRequestHandler.doGetAppliedProcessors.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/processors`, this.centerRequestHandler.doAddProcessor.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id/processors`, this.centerRequestHandler.doRemoveProcessor.bind(this.centerRequestHandler));
this.app.get(`/api/${centerApiPath}`, this.centerRequestHandler.doGet.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}`, this.centerRequestHandler.doPost.bind(this.centerRequestHandler));
this.app.put(`/api/${centerApiPath}/:id/send`, this.centerRequestHandler.doConfigureSingleJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/send/default`, this.centerRequestHandler.doSendSingleJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/send`, this.centerRequestHandler.doSend.bind(this.centerRequestHandler));
this.app.put(`/api/${centerApiPath}/:id/sendMany`, this.centerRequestHandler.doConfigureManyJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/sendMany/default`, this.centerRequestHandler.doSendManyJob.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/sendMany`, this.centerRequestHandler.doSendMany.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id/sendMany`, this.centerRequestHandler.doCancelSendMany.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id/connect`, this.centerRequestHandler.doDisconnect.bind(this.centerRequestHandler));
this.app.get(`/api/${centerApiPath}/processors`, this.centerRequestHandler.doGetAvailableProcessors.bind(this.centerRequestHandler));
this.app.get(`/api/${centerApiPath}/:id/processors`, this.centerRequestHandler.doGetAppliedProcessors.bind(this.centerRequestHandler));
this.app.post(`/api/${centerApiPath}/:id/processors`, this.centerRequestHandler.doAddProcessor.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id/processors`, this.centerRequestHandler.doRemoveProcessor.bind(this.centerRequestHandler));
this.app.get(`/api/${centerApiPath}/:id`, this.centerRequestHandler.doGetById.bind(this.centerRequestHandler));
this.app.patch(`/api/${centerApiPath}/:id`, this.centerRequestHandler.doPatch.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id`, this.centerRequestHandler.doDelete.bind(this.centerRequestHandler));
this.app.get(`/api/${centerApiPath}/:id`, this.centerRequestHandler.doGetById.bind(this.centerRequestHandler));
this.app.patch(`/api/${centerApiPath}/:id`, this.centerRequestHandler.doPatch.bind(this.centerRequestHandler));
this.app.delete(`/api/${centerApiPath}/:id`, this.centerRequestHandler.doDelete.bind(this.centerRequestHandler));
this.app.get('/api/ping', function (req: any, res: any) {
res.send('pong');
});
this.app.get('/api/ping', function (req: any, res: any) {
res.send('pong');
});
this.server = this.app.listen(SERVER_PORT, function () {
// @ts-ignore
this.logger.log1(`HTTPServer listening at http://localhost:${SERVER_PORT}`)
}.bind(this));
}
this.server = this.app.listen(SERVER_PORT, function () {
// @ts-ignore
this.logger.log1(`HTTPServer listening at http://localhost:${SERVER_PORT}`)
}.bind(this));
}
}

View File

@@ -4,156 +4,156 @@ import SessionManager from "../SessionManager";
import SmppSession from "../SmppSession";
export default abstract class RequestHandler {
abstract sessionManager: SessionManager;
logger: Logger = new Logger(this.constructor.name);
abstract sessionManager: SessionManager;
logger: Logger = new Logger(this.constructor.name);
doGet(req: any, res: any): void {
this.logger.log1(`Getting sessions`);
res.send(this.sessionManager.serialize());
}
doGet(req: any, res: any): void {
this.logger.log1(`Getting sessions`);
res.send(this.sessionManager.serialize());
}
doGetById(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Session found with ID ${req.params.id}`)
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doGetById(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Session found with ID ${req.params.id}`)
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doPatch(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Session found with ID ${req.params.id}`)
if (!!req.body.username && req.body.username !== session.username) {
session.username = req.body.username;
}
if (!!req.body.password && req.body.password !== session.password) {
session.password = req.body.password;
}
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doPatch(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Session found with ID ${req.params.id}`)
if (!!req.body.username && req.body.username !== session.username) {
session.username = req.body.username;
}
if (!!req.body.password && req.body.password !== session.password) {
session.password = req.body.password;
}
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doConfigureSingleJob(req: any, res: any): void {
this.sessionManager.getSession(Number(req.params.id)).then((session: SmppSession) => {
let job: Job = session.defaultSingleJob;
job.update(req);
this.logger.log1(`Updating default job on session with ID ${req.params.id}`);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doConfigureSingleJob(req: any, res: any): void {
this.sessionManager.getSession(Number(req.params.id)).then((session: SmppSession) => {
let job: Job = session.defaultSingleJob;
job.update(req);
this.logger.log1(`Updating default job on session with ID ${req.params.id}`);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doSendSingleJob(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending pre-configured message on session with ID ${req.params.id}`);
session.sendSingleDefault()
.then(pdu => res.send(pdu),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doSendSingleJob(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending pre-configured message on session with ID ${req.params.id}`);
session.sendSingleDefault()
.then(pdu => res.send(pdu),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doSend(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending message on session with ID ${req.params.id}`);
let tempJob: Job = JSON.parse(JSON.stringify(session.defaultSingleJob));
tempJob.pdu.source_addr = req.body.source;
tempJob.pdu.destination_addr = req.body.destination;
tempJob.pdu.short_message = req.body.message;
session.sendSingle(tempJob)
.then(pdu => res.send(pdu),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doSend(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending message on session with ID ${req.params.id}`);
let tempJob: Job = JSON.parse(JSON.stringify(session.defaultSingleJob));
tempJob.pdu.source_addr = req.body.source;
tempJob.pdu.destination_addr = req.body.destination;
tempJob.pdu.short_message = req.body.message;
session.sendSingle(tempJob)
.then(pdu => res.send(pdu),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doConfigureManyJob(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let job: Job = session.defaultMultipleJob;
job.update(req);
this.logger.log1(`Updating default job on session with ID ${req.params.id}`);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doConfigureManyJob(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
let job: Job = session.defaultMultipleJob;
job.update(req);
this.logger.log1(`Updating default job on session with ID ${req.params.id}`);
res.send(session.serialize());
}, this.handleSessionNotFound.bind(this, req, res));
}
doSendManyJob(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending pre-configured messages on session with ID ${req.params.id}`);
session.sendMultipleDefault()
.then(() => res.send({}),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doSendManyJob(req: any, res: any): void {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending pre-configured messages on session with ID ${req.params.id}`);
session.sendMultipleDefault()
.then(() => res.send({}),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doSendMany(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending message on session with ID ${req.params.id}`);
let tempJob: Job = JSON.parse(JSON.stringify(session.defaultMultipleJob));
tempJob.pdu.source_addr = req.body.source;
tempJob.pdu.destination_addr = req.body.destination;
tempJob.pdu.short_message = req.body.message;
tempJob.perSecond = 1 / (req.body.interval / 1000);
tempJob.count = req.body.count;
session.sendMultiple(tempJob)
.then(pdu => res.send(pdu),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doSendMany(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Sending message on session with ID ${req.params.id}`);
let tempJob: Job = JSON.parse(JSON.stringify(session.defaultMultipleJob));
tempJob.pdu.source_addr = req.body.source;
tempJob.pdu.destination_addr = req.body.destination;
tempJob.pdu.short_message = req.body.message;
tempJob.perSecond = 1 / (req.body.interval / 1000);
tempJob.count = req.body.count;
session.sendMultiple(tempJob)
.then(pdu => res.send(pdu),
err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doCancelSendMany(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Cancelling send timer for session with ID ${req.params.id}`);
session.cancelSendInterval();
res.send({});
}, this.handleSessionNotFound.bind(this, req, res));
}
doCancelSendMany(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Cancelling send timer for session with ID ${req.params.id}`);
session.cancelSendInterval();
res.send({});
}, this.handleSessionNotFound.bind(this, req, res));
}
doDisconnect(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Disconnecting session with ID ${req.params.id}`)
session.close().then(() => res.send(session.serialize()), err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doDisconnect(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Disconnecting session with ID ${req.params.id}`)
session.close().then(() => res.send(session.serialize()), err => res.status(400).send({
err: true,
message: err
}));
}, this.handleSessionNotFound.bind(this, req, res));
}
doDelete(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Deleting session with ID ${req.params.id}`);
this.sessionManager.removeSession(session);
res.send({});
}, this.handleSessionNotFound.bind(this, req, res));
}
doDelete(req: any, res: any) {
this.sessionManager.getSession(req.params.id).then((session: SmppSession) => {
this.logger.log1(`Deleting session with ID ${req.params.id}`);
this.sessionManager.removeSession(session);
res.send({});
}, this.handleSessionNotFound.bind(this, req, res));
}
abstract doPost(req: any, res: any): void;
abstract doPost(req: any, res: any): void;
abstract doConnect(req: any, res: any): void;
abstract doConnect(req: any, res: any): void;
abstract doBind(req: any, res: any): void;
abstract doBind(req: any, res: any): void;
abstract doGetAvailableProcessors(req: any, res: any): void;
abstract doGetAvailableProcessors(req: any, res: any): void;
abstract doGetAppliedProcessors(req: any, res: any): void;
abstract doGetAppliedProcessors(req: any, res: any): void;
abstract doAddProcessor(req: any, res: any): void;
abstract doAddProcessor(req: any, res: any): void;
abstract doRemoveProcessor(req: any, res: any): void;
abstract doRemoveProcessor(req: any, res: any): void;
handleSessionNotFound(req: any, res: any): void {
let error = `No session found with ID ${req.params.id}`;
this.logger.log1(error);
res.status(404).send({
err: true,
message: error
});
}
handleSessionNotFound(req: any, res: any): void {
let error = `No session found with ID ${req.params.id}`;
this.logger.log1(error);
res.status(404).send({
err: true,
message: error
});
}
}

View File

@@ -4,102 +4,102 @@ import {PDU, SerializedJob} from "../CommonObjects";
const smpp = require("smpp");
export default class Job {
static readonly STATE_CHANGED: string = "STATE_CHANGED";
private eventEmitter: EventEmitter = new EventEmitter();
static readonly STATE_CHANGED: string = "STATE_CHANGED";
private eventEmitter: EventEmitter = new EventEmitter();
constructor(pdu: PDU, perSecond?: number, count?: number) {
Job.pduParseShortMessage(pdu);
this._pdu = pdu;
this._perSecond = perSecond;
this._count = count;
}
constructor(pdu: PDU, perSecond?: number, count?: number) {
Job.pduParseShortMessage(pdu);
this._pdu = pdu;
this._perSecond = perSecond;
this._count = count;
}
private _pdu: PDU;
private _pdu: PDU;
get pdu(): PDU {
return this._pdu;
}
get pdu(): PDU {
return this._pdu;
}
set pdu(value: PDU) {
this._pdu = value;
this.eventEmitter.emit(Job.STATE_CHANGED, {});
}
set pdu(value: PDU) {
this._pdu = value;
this.eventEmitter.emit(Job.STATE_CHANGED, {});
}
private _perSecond?: number;
private _perSecond?: number;
get perSecond(): number {
return <number>this._perSecond;
}
get perSecond(): number {
return <number>this._perSecond;
}
set perSecond(value: number) {
this._perSecond = value;
this.eventEmitter.emit(Job.STATE_CHANGED, {});
}
set perSecond(value: number) {
this._perSecond = value;
this.eventEmitter.emit(Job.STATE_CHANGED, {});
}
private _count?: number;
private _count?: number;
get count(): number {
return <number>this._count;
}
get count(): number {
return <number>this._count;
}
set count(value: number) {
this._count = value;
this.eventEmitter.emit(Job.STATE_CHANGED, {});
}
set count(value: number) {
this._count = value;
this.eventEmitter.emit(Job.STATE_CHANGED, {});
}
static pduParseShortMessage(pdu: PDU) {
if (pdu.short_message && pdu.short_message.type === "Buffer") {
pdu.short_message = Buffer.from(pdu.short_message.data, 'ascii').toString();
}
if (typeof pdu.short_message === "object") {
pdu.short_message = pdu.short_message.toString();
}
}
static pduParseShortMessage(pdu: PDU) {
if (pdu.short_message && pdu.short_message.type === "Buffer") {
pdu.short_message = Buffer.from(pdu.short_message.data, 'ascii').toString();
}
if (typeof pdu.short_message === "object") {
pdu.short_message = pdu.short_message.toString();
}
}
static createEmptySingle(command: string): Job {
let pdu1 = new smpp.PDU(command, {});
Job.pduParseShortMessage(pdu1);
return new Job(pdu1);
}
static createEmptySingle(command: string): Job {
let pdu1 = new smpp.PDU(command, {});
Job.pduParseShortMessage(pdu1);
return new Job(pdu1);
}
static createEmptyMultiple(command: string): Job {
let pdu1 = new smpp.PDU(command, {});
Job.pduParseShortMessage(pdu1);
return new Job(pdu1, 1, 1);
}
static createEmptyMultiple(command: string): Job {
let pdu1 = new smpp.PDU(command, {});
Job.pduParseShortMessage(pdu1);
return new Job(pdu1, 1, 1);
}
static deserialize(serialized: SerializedJob): Job {
let pdu: PDU = new smpp.PDU(serialized.pdu.command, serialized.pdu);
return new Job(pdu, serialized.perSecond, serialized.count);
}
static deserialize(serialized: SerializedJob): Job {
let pdu: PDU = new smpp.PDU(serialized.pdu.command, serialized.pdu);
return new Job(pdu, serialized.perSecond, serialized.count);
}
update(req: any): void {
if (req.body.source != this._pdu.source_addr) {
this._pdu.source_addr = req.body.source;
}
if (req.body.destination != this._pdu.destination_addr) {
this._pdu.destination_addr = req.body.destination;
}
if (req.body.message != this._pdu.short_message) {
this._pdu.short_message = req.body.message;
}
if (!!this._perSecond && !!req.body.perSecond && req.body.perSecond != this._perSecond) {
this._perSecond = req.body.perSecond;
}
if (!!this._count && !!req.body.count && req.body.count != this._count) {
this._count = req.body.count;
}
}
update(req: any): void {
if (req.body.source != this._pdu.source_addr) {
this._pdu.source_addr = req.body.source;
}
if (req.body.destination != this._pdu.destination_addr) {
this._pdu.destination_addr = req.body.destination;
}
if (req.body.message != this._pdu.short_message) {
this._pdu.short_message = req.body.message;
}
if (!!this._perSecond && !!req.body.perSecond && req.body.perSecond != this._perSecond) {
this._perSecond = req.body.perSecond;
}
if (!!this._count && !!req.body.count && req.body.count != this._count) {
this._count = req.body.count;
}
}
serialize(): SerializedJob {
return {
pdu: this.pdu,
perSecond: this.perSecond,
count: this.count
};
}
serialize(): SerializedJob {
return {
pdu: this.pdu,
perSecond: this.perSecond,
count: this.count
};
}
on(event: string, callback: (...args: any[]) => void): void {
this.eventEmitter.on(event, callback);
}
on(event: string, callback: (...args: any[]) => void): void {
this.eventEmitter.on(event, callback);
}
}

View File

@@ -6,63 +6,63 @@ const LOG_LEVEL: number = Number(process.env.LOG_LEVEL) || 0;
const LOG_FILE: string = process.env.LOG_FILE || "";
export default class Logger {
log1 = this.log.bind(this, 1);
log2 = this.log.bind(this, 2);
log3 = this.log.bind(this, 3);
log4 = this.log.bind(this, 4);
log5 = this.log.bind(this, 5);
log6 = this.log.bind(this, 6);
private clazz: string;
private readonly logLevel: number;
private readonly logFile: string;
private readonly logFileWriteStream: WriteStream | null = null;
log1 = this.log.bind(this, 1);
log2 = this.log.bind(this, 2);
log3 = this.log.bind(this, 3);
log4 = this.log.bind(this, 4);
log5 = this.log.bind(this, 5);
log6 = this.log.bind(this, 6);
private clazz: string;
private readonly logLevel: number;
private readonly logFile: string;
private readonly logFileWriteStream: WriteStream | null = null;
constructor(clazz: string) {
this.clazz = clazz;
this.logLevel = LOG_LEVEL;
this.logFile = LOG_FILE;
constructor(clazz: string) {
this.clazz = clazz;
this.logLevel = LOG_LEVEL;
this.logFile = LOG_FILE;
if (!!this.logFile) {
this.logFileWriteStream = fs.createWriteStream(this.logFile, {flags: 'a'});
}
}
if (!!this.logFile) {
this.logFileWriteStream = fs.createWriteStream(this.logFile, {flags: 'a'});
}
}
leftPad(str: string, len: number, char: string): string {
str = String(str);
let i: number = -1;
len = len - str.length;
if (char === undefined) {
char = " ";
}
while (++i < len) {
str = char + str;
}
return str;
}
leftPad(str: string, len: number, char: string): string {
str = String(str);
let i: number = -1;
len = len - str.length;
if (char === undefined) {
char = " ";
}
while (++i < len) {
str = char + str;
}
return str;
}
log(logLevel: number, data: any): void {
if (typeof data === "object") {
data = JSON.stringify(data);
}
let date = new Date();
log(logLevel: number, data: any): void {
if (typeof data === "object") {
data = JSON.stringify(data);
}
let date = new Date();
let year: string = this.leftPad(String(date.getFullYear()), 4, '0');
let month: string = this.leftPad(String(date.getMonth() + 1), 2, '0');
let day: string = this.leftPad(String(date.getDate()), 2, '0');
let year: string = this.leftPad(String(date.getFullYear()), 4, '0');
let month: string = this.leftPad(String(date.getMonth() + 1), 2, '0');
let day: string = this.leftPad(String(date.getDate()), 2, '0');
let hours: string = this.leftPad(String(date.getHours()), 2, '0');
let minutes: string = this.leftPad(String(date.getMinutes()), 2, '0');
let seconds: string = this.leftPad(String(date.getSeconds()), 2, '0');
let milliseconds: string = this.leftPad(String(date.getMilliseconds()), 3, '0');
let hours: string = this.leftPad(String(date.getHours()), 2, '0');
let minutes: string = this.leftPad(String(date.getMinutes()), 2, '0');
let seconds: string = this.leftPad(String(date.getSeconds()), 2, '0');
let milliseconds: string = this.leftPad(String(date.getMilliseconds()), 3, '0');
let datePrefix: string = `[${day}/${month}/${year}-${hours}:${minutes}:${seconds}:${milliseconds}]`
let datePrefix: string = `[${day}/${month}/${year}-${hours}:${minutes}:${seconds}:${milliseconds}]`
let out: string = datePrefix.padEnd(30, ' ') + `[${this.clazz}]`.padEnd(28, ' ') + `(${logLevel})`.padEnd(8, ' ') + data;
if (logLevel <= this.logLevel) {
console.log(out);
}
if (!!this.logFileWriteStream) {
this.logFileWriteStream.write(out + "\n");
}
}
let out: string = datePrefix.padEnd(30, ' ') + `[${this.clazz}]`.padEnd(28, ' ') + `(${logLevel})`.padEnd(8, ' ') + data;
if (logLevel <= this.logLevel) {
console.log(out);
}
if (!!this.logFileWriteStream) {
this.logFileWriteStream.write(out + "\n");
}
}
}

View File

@@ -1,17 +1,17 @@
import {PDU} from "./CommonObjects";
export default class MessageIdManager {
private static messages: { [key: string]: number } = {};
private static messages: { [key: string]: number } = {};
static addMessageId(message: PDU, id: number): void {
this.messages[this.getMessageHash(message)] = id;
}
static addMessageId(message: PDU, id: number): void {
this.messages[this.getMessageHash(message)] = id;
}
static getMessageId(message: PDU): number | undefined {
return this.messages[this.getMessageHash(message)];
}
static getMessageId(message: PDU): number | undefined {
return this.messages[this.getMessageHash(message)];
}
private static getMessageHash(message: PDU): string {
return btoa(`${message.source_addr}:${message.destination_addr}:${message.short_message}`);
}
private static getMessageHash(message: PDU): string {
return btoa(`${message.source_addr}:${message.destination_addr}:${message.short_message}`);
}
}

View File

@@ -2,36 +2,36 @@ import Logger from "../Logger";
import SmppSession from "../SmppSession";
export default abstract class PduProcessor {
readonly abstract type: string
readonly sessionType: string;
readonly name: string = this.constructor.name;
readonly logger: Logger = new Logger(`PduProcessor: ${this.name}`);
abstract applicableCommands: string[];
readonly abstract type: string
readonly sessionType: string;
readonly name: string = this.constructor.name;
readonly logger: Logger = new Logger(`PduProcessor: ${this.name}`);
abstract applicableCommands: string[];
constructor(type: string) {
this.sessionType = type;
}
constructor(type: string) {
this.sessionType = type;
}
protected pduDoesApply(pdu: any): boolean {
if (pdu.command) {
return this.applicableCommands.includes(pdu.command);
}
return false;
}
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): any {
if (this.pduDoesApply(pdu)) {
return this.doProcess(session, pdu, entity);
}
}
protected abstract doProcess(session: any, pdu: any, entity?: SmppSession | undefined): any;
serialize(): object {
return {
servesSessionType: this.sessionType,
name: this.name,
type: this.type
};
}
processPdu(session: any, pdu: any, entity?: SmppSession | undefined): any {
if (this.pduDoesApply(pdu)) {
return this.doProcess(session, pdu, entity);
}
}
protected pduDoesApply(pdu: any): boolean {
if (pdu.command) {
return this.applicableCommands.includes(pdu.command);
}
return false;
}
serialize(): object {
return {
servesSessionType: this.sessionType,
name: this.name,
type: this.type
};
}
protected abstract doProcess(session: any, pdu: any, entity?: SmppSession | undefined): any;
}

View File

@@ -4,42 +4,42 @@ import Postprocessor from "../Postprocessor";
const smpp = require("smpp");
export default class BindTranscieverReplyProcessor extends Postprocessor {
applicableCommands: string[] = ['bind_transceiver'];
applicableCommands: string[] = ['bind_transceiver'];
constructor(type: string) {
super(type);
}
constructor(type: string) {
super(type);
}
doProcess(session: any, pdu: any, entity?: Center | undefined): Promise<any> {
return new Promise((resolve, reject) => {
if (!entity) {
reject();
}
doProcess(session: any, pdu: any, entity?: Center | undefined): Promise<any> {
return new Promise((resolve, reject) => {
if (!entity) {
reject();
}
this.logger.log1(`Center-${entity?.id} got a bind_transceiver with system_id ${pdu.system_id} and password ${pdu.password}`);
session.pause();
if (pdu.system_id === entity?.username && pdu.password === entity?.password) {
this.logger.log1(`Center-${entity?.id} client connection successful`);
if (pdu.response) {
entity?.doSendPdu(pdu.response(), session);
}
session.resume();
// @ts-ignore
entity?.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
entity?.sessions.push(session);
entity?.updateStatus();
} else {
this.logger.log1(`Center-${entity?.id} client connection failed, invalid credentials (expected: ${entity?.username}, ${entity?.password})`);
if (pdu.response) {
entity?.doSendPdu(pdu.response({
command_status: smpp.ESME_RBINDFAIL
}), session);
}
// @ts-ignore
entity?.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
entity?.updateStatus();
session.close();
}
});
}
this.logger.log1(`Center-${entity?.id} got a bind_transceiver with system_id ${pdu.system_id} and password ${pdu.password}`);
session.pause();
if (pdu.system_id === entity?.username && pdu.password === entity?.password) {
this.logger.log1(`Center-${entity?.id} client connection successful`);
if (pdu.response) {
entity?.doSendPdu(pdu.response(), session);
}
session.resume();
// @ts-ignore
entity.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
entity?.sessions.push(session);
entity?.updateStatus();
} else {
this.logger.log1(`Center-${entity?.id} client connection failed, invalid credentials (expected: ${entity?.username}, ${entity?.password})`);
if (pdu.response) {
entity?.doSendPdu(pdu.response({
command_status: smpp.ESME_RBINDFAIL
}), session);
}
// @ts-ignore
entity.pendingSessions = entity?.pendingSessions.filter((s) => s !== session);
entity?.updateStatus();
session.close();
}
});
}
}

View File

@@ -6,38 +6,44 @@ const smpp = require("smpp");
export default class DeliveryReceiptProcessor extends Postprocessor {
applicableCommands: string[] = ['submit_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (pdu.registered_delivery) {
let drMessage: string = "";
let date: string = new Date().toISOString().replace(/T/, '').replace(/\..+/, '').replace(/-/g, '').replace(/:/g, '').substring(2, 12);
constructor(type: string) {
super(type);
}
let relatedMessageId: number | undefined = MessageIdManager.getMessageId(pdu);
if (relatedMessageId) {
drMessage += "id:" + relatedMessageId + " ";
drMessage += "sub:001 ";
drMessage += "dlvrd:001 ";
drMessage += "submit date:" + date + " ";
drMessage += "done date:" + date + " ";
drMessage += "stat:DELIVRD ";
drMessage += "err:000 ";
drMessage += "text:";
sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
let DRPdu = new smpp.PDU('deliver_sm', {
source_addr: pdu.source_addr,
destination_addr: pdu.destination_addr,
short_message: drMessage,
esm_class: 4,
});
entity?.doSendPdu(DRPdu, session);
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
let drMessage: string = "";
let date: string = new Date().toISOString().replace(/T/, '').replace(/\..+/, '').replace(/-/g, '').replace(/:/g, '').substring(2, 12);
resolve(pdu);
}
}
});
}
let relatedMessageId: number | undefined = MessageIdManager.getMessageId(pdu);
if (relatedMessageId) {
drMessage += "id:" + relatedMessageId + " ";
drMessage += "sub:001 ";
drMessage += "dlvrd:001 ";
drMessage += "submit date:" + date + " ";
drMessage += "done date:" + date + " ";
drMessage += "stat:DELIVRD ";
drMessage += "err:000 ";
drMessage += "text:";
let sleepTime = 0;
let DRPdu = new smpp.PDU('deliver_sm', {
source_addr: pdu.source_addr,
destination_addr: pdu.destination_addr,
short_message: drMessage,
esm_class: 4,
});
setTimeout(() => entity?.doSendPdu(DRPdu, session), sleepTime);
resolve(pdu);
}
});
}
}

View File

@@ -5,20 +5,21 @@ const smpp = require("smpp");
export default class EchoPduProcessor extends Postprocessor {
applicableCommands: string[] = ['submit_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
// Temporary (?) safeguard against echoing long sms
if (!pdu.short_message.udh) {
let echoPdu = new smpp.PDU('deliver_sm', {...pdu});
echoPdu.source_addr = pdu.destination_addr;
echoPdu.destination_addr = pdu.source_addr;
entity?.doSendPdu(echoPdu, session);
resolve(echoPdu);
}
});
}
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
// Temporary (?) safeguard against echoing long sms
if (!pdu.short_message.udh) {
let echoPdu = new smpp.PDU('deliver_sm', {...pdu});
echoPdu.source_addr = pdu.destination_addr;
echoPdu.destination_addr = pdu.source_addr;
entity?.doSendPdu(echoPdu, session);
resolve(echoPdu);
}
});
}
}

View File

@@ -2,16 +2,16 @@ import SmppSession from "../../../SmppSession";
import Postprocessor from "../Postprocessor";
export default class EnquireLinkReplyProcessor extends Postprocessor {
applicableCommands: string[] = ['enquire_link'];
applicableCommands: string[] = ['enquire_link'];
constructor(type: string) {
super(type);
}
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise((resolve, reject) => {
entity?.doSendPdu(pdu.response(), session);
resolve(pdu);
});
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise((resolve, reject) => {
entity?.doSendPdu(pdu.response(), session);
resolve(pdu);
});
}
}

View File

@@ -3,20 +3,20 @@ import SmppSession from "../../../SmppSession";
import Postprocessor from "../Postprocessor";
export default class SubmitSmReplyProcessor extends Postprocessor {
applicableCommands: string[] = ['submit_sm'];
private messageIdIterator: number = 0;
applicableCommands: string[] = ['submit_sm'];
private messageIdIterator: number = 0;
constructor(type: string) {
super(type);
}
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise((resolve, reject) => {
let response = pdu.response();
response.message_id = this.messageIdIterator++;
MessageIdManager.addMessageId(pdu, response.message_id);
entity?.doSendPdu(response, session);
resolve(pdu);
});
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise((resolve, reject) => {
let response = pdu.response();
response.message_id = this.messageIdIterator++;
MessageIdManager.addMessageId(pdu, response.message_id);
entity?.doSendPdu(response, session);
resolve(pdu);
});
}
}

View File

@@ -2,16 +2,16 @@ import SmppSession from "../../../SmppSession";
import Postprocessor from "../Postprocessor";
export default class DeliverSmReplyProcessor extends Postprocessor {
applicableCommands: string[] = ['deliver_sm'];
applicableCommands: string[] = ['deliver_sm'];
constructor(type: string) {
super(type);
}
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise((resolve, reject) => {
entity?.doSendPdu(pdu.response(), session);
resolve(pdu);
});
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise((resolve, reject) => {
entity?.doSendPdu(pdu.response(), session);
resolve(pdu);
});
}
}

View File

@@ -1,5 +1,5 @@
import PduProcessor from "../PduProcessor";
export default abstract class Postprocessor extends PduProcessor {
readonly type: string = Postprocessor.name;
readonly type: string = Postprocessor.name;
}

View File

@@ -2,15 +2,15 @@ import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class DeliveryReceiptRequestProcessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm'];
applicableCommands: string[] = ['submit_sm'];
constructor(type: string) {
super(type);
}
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.registered_delivery = 1;
});
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.registered_delivery = 1;
});
}
}

View File

@@ -3,21 +3,21 @@ import Preprocessor from "../Preprocessor";
export default class DestinationEnumeratorProcessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
private iterator: number = 0;
private iterator: number = 0;
constructor(type: string) {
super(type);
}
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (!!pdu.destination_addr) {
pdu.destination_addr = pdu.destination_addr + this.padLeft(String(this.iterator++), '0', 5);
}
});
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (!!pdu.destination_addr) {
pdu.destination_addr = pdu.destination_addr + this.padLeft(String(this.iterator++), '0', 5);
}
});
}
private padLeft(str: string, pad: string, length: number): string {
return (new Array(length + 1).join(pad) + str).slice(-length);
}
private padLeft(str: string, pad: string, length: number): string {
return (new Array(length + 1).join(pad) + str).slice(-length);
}
}

View File

@@ -0,0 +1,40 @@
import {PDU} from "../../../CommonObjects";
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class DestinationSetPreprocessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
private sourceSet: string[] = [];
constructor(type: string) {
super(type);
while (this.sourceSet.length < 100) {
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
}
}
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (pdu.short_message) {
if (pdu.short_message.includes("arg:")) {
let temp: string = pdu.short_message.split(";");
let arg: number = Number(temp[0].split(":")[1]);
while (this.sourceSet.length < arg) {
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
}
while (this.sourceSet.length > arg) {
this.sourceSet.pop();
}
pdu.short_message = temp[1];
}
}
pdu.destination_addr = pdu.destination_addr + this.sourceSet[this.getRandomInt(0, this.sourceSet.length)];
});
}
private getRandomInt(min: number, max: number): number {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
}

View File

@@ -0,0 +1,17 @@
import {PDU} from "../../../CommonObjects";
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class GSM0338Preprocessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.data_coding = 0xf6;
});
}
}

View File

@@ -5,83 +5,83 @@ import Preprocessor from "../Preprocessor";
const smpp = require('smpp');
export default class LongSmsProcessor extends Preprocessor {
static readonly maxMessageSizeBits = 1072;
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
static readonly maxMessageSizeBits = 1072;
private iterator: number = 0;
private iterator: number = 0;
constructor(type: string) {
// An sms can have a maximum length (short_message) of 1120 bits or 140 bytes.
// For a message encoded in UCS-2, the maximum length is 70 characters. (2 bytes per character)
// For a message encoded in GSM-7, the maximum length is 160 characters. (7 bits per character)
// Once a message is split the part information is placed into udh
// Like
// destination_addr: config.destinationAddress,
// short_message: {
// udh: Buffer.from([0x05, 0x00, 0x03, 0x05, 0x02, 0x01]),
// message: "Hello World!"
// }
// The UDH parameters are as follows:
// 0x05: Length of UDH (5 bytes to follow)
// 0x00: Concatenated message Information Element (8-bit reference number)
// 0x03: Length of Information Element data (3 bytes to follow)
// 0xXX: Reference number for this concatenated message
// 0xYY: Number of fragments in the concatenated message
// 0xZZ: Fragment number/index within the concatenated message 1072
super(type);
}
constructor(type: string) {
// An sms can have a maximum length (short_message) of 1120 bits or 140 bytes.
// For a message encoded in UCS-2, the maximum length is 70 characters. (2 bytes per character)
// For a message encoded in GSM-7, the maximum length is 160 characters. (7 bits per character)
// Once a message is split the part information is placed into udh
// Like
// destination_addr: config.destinationAddress,
// short_message: {
// udh: Buffer.from([0x05, 0x00, 0x03, 0x05, 0x02, 0x01]),
// message: "Hello World!"
// }
// The UDH parameters are as follows:
// 0x05: Length of UDH (5 bytes to follow)
// 0x00: Concatenated message Information Element (8-bit reference number)
// 0x03: Length of Information Element data (3 bytes to follow)
// 0xXX: Reference number for this concatenated message
// 0xYY: Number of fragments in the concatenated message
// 0xZZ: Fragment number/index within the concatenated message 1072
super(type);
}
static getCharacterSizeForEncoding(pdu: PDU) {
let encoding: number | undefined = pdu.data_coding;
if (!encoding) {
encoding = 0;
}
let characterSizeBits: number = 0;
switch (encoding) {
case 0:
case 1:
characterSizeBits = 8;
break;
case 8:
characterSizeBits = 16;
break;
}
return characterSizeBits;
}
static getCharacterSizeForEncoding(pdu: PDU) {
let encoding: number | undefined = pdu.data_coding;
if (!encoding) {
encoding = 0;
}
let characterSizeBits: number = 0;
switch (encoding) {
case 0:
case 1:
characterSizeBits = 8;
break;
case 8:
characterSizeBits = 16;
break;
}
return characterSizeBits;
}
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (!!pdu.short_message) {
let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
let maxMessageLength: number = LongSmsProcessor.maxMessageSizeBits / characterSizeBits;
if (characterSizeBits) {
let splitMessage: string[] = [];
let message: string = pdu.short_message;
let messageLength: number = message.length;
let messageCount: number = Math.ceil(messageLength / maxMessageLength);
for (let i = 0; i < messageCount; i++) {
splitMessage.push(message.substr(i * maxMessageLength, maxMessageLength));
}
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (!!pdu.short_message) {
let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
let maxMessageLength: number = LongSmsProcessor.maxMessageSizeBits / characterSizeBits;
if (characterSizeBits) {
let splitMessage: string[] = [];
let message: string = pdu.short_message;
let messageLength: number = message.length;
let messageCount: number = Math.ceil(messageLength / maxMessageLength);
for (let i = 0; i < messageCount; i++) {
splitMessage.push(message.substr(i * maxMessageLength, maxMessageLength));
}
splitMessage.forEach((messagePart: string, index: number) => {
let udh: Buffer = Buffer.from([0x05, 0x00, 0x03, this.iterator++, messageCount, index + 1]);
splitMessage.forEach((messagePart: string, index: number) => {
let udh: Buffer = Buffer.from([0x05, 0x00, 0x03, this.iterator++, messageCount, index + 1]);
if (index < (messageCount - 1)) {
let partPdu = new smpp.PDU(pdu.command, {...pdu});
partPdu.short_message = {
udh: udh,
message: messagePart
}
entity?.doSendPdu(partPdu, session);
} else {
pdu.short_message = {
udh: udh,
message: messagePart
}
resolve(pdu);
}
});
}
}
});
}
if (index < (messageCount - 1)) {
let partPdu = new smpp.PDU(pdu.command, {...pdu});
partPdu.short_message = {
udh: udh,
message: messagePart
}
entity?.doSendPdu(partPdu, session);
} else {
pdu.short_message = {
udh: udh,
message: messagePart
}
resolve(pdu);
}
});
}
}
});
}
}

View File

@@ -0,0 +1,16 @@
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class ProtocolId2DigitProcessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.protocol_id = 16;
});
}
}

View File

@@ -0,0 +1,16 @@
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class ProtocolId3DigitProcessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.protocol_id = 128;
});
}
}

View File

@@ -0,0 +1,17 @@
import {PDU} from "../../../CommonObjects";
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class ProtocolId4DigitProcessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.data_coding = 2048;
});
}
}

View File

@@ -0,0 +1,16 @@
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class ProtocolIdProcessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.protocol_id = 1;
});
}
}

View File

@@ -2,22 +2,22 @@ import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class SourceEnumeratorProcessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
private iterator: number = 0;
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
private iterator: number = 0;
constructor(type: string) {
super(type);
}
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (!!pdu.source_addr) {
pdu.source_addr = pdu.source_addr + this.padLeft(String(this.iterator++), '0', 5);
}
});
}
protected doProcess(session: any, pdu: any, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (!!pdu.source_addr) {
pdu.source_addr = pdu.source_addr + this.padLeft(String(this.iterator++), '0', 5);
}
});
}
private padLeft(str: string, pad: string, length: number): string {
return (new Array(length + 1).join(pad) + str).slice(-length);
}
private padLeft(str: string, pad: string, length: number): string {
return (new Array(length + 1).join(pad) + str).slice(-length);
}
}

View File

@@ -0,0 +1,40 @@
import {PDU} from "../../../CommonObjects";
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class SourceSetPreprocessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
private sourceSet: string[] = [];
constructor(type: string) {
super(type);
while (this.sourceSet.length < 100) {
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
}
}
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
if (pdu.short_message) {
if (pdu.short_message.includes("arg:")) {
let temp: string = pdu.short_message.split(";");
let arg: number = Number(temp[0].split(":")[1]);
while (this.sourceSet.length < arg) {
this.sourceSet.push(this.getRandomInt(100000, 999999).toString());
}
while (this.sourceSet.length > arg) {
this.sourceSet.pop();
}
pdu.short_message = temp[1];
}
}
pdu.source_addr = pdu.source_addr + this.sourceSet[this.getRandomInt(0, this.sourceSet.length)];
});
}
private getRandomInt(min: number, max: number): number {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
}

View File

@@ -0,0 +1,17 @@
import {PDU} from "../../../CommonObjects";
import SmppSession from "../../../SmppSession";
import Preprocessor from "../Preprocessor";
export default class UCS2Preprocessor extends Preprocessor {
applicableCommands: string[] = ['submit_sm', 'deliver_sm'];
constructor(type: string) {
super(type);
}
protected doProcess(session: any, pdu: PDU, entity?: SmppSession | undefined): Promise<any> {
return new Promise<any>((resolve, reject) => {
pdu.data_coding = 8;
});
}
}

View File

@@ -1,5 +1,5 @@
import PduProcessor from "../PduProcessor";
export default abstract class Preprocessor extends PduProcessor {
readonly type: string = Preprocessor.name;
readonly type: string = Preprocessor.name;
}

View File

@@ -12,98 +12,120 @@ import DeliverSmReplyProcessor from "./Postprocessor/Client/DeliverSmReplyProces
import Postprocessor from "./Postprocessor/Postprocessor";
import DeliveryReceiptRequestProcessor from "./Preprocessor/Client/DeliveryReceiptRequestProcessor";
import DestinationEnumeratorProcessor from "./Preprocessor/Client/DestinationEnumeratorProcessor";
import DestinationSetPreprocessor from "./Preprocessor/Client/DestinationSetPreprocessor";
import GSM0338Preprocessor from "./Preprocessor/Client/GSM0338Preprocessor";
import LongSmsProcessor from "./Preprocessor/Client/LongSmsProcessor";
import ProtocolId2DigitProcessor from "./Preprocessor/Client/ProtocolId-2Digit-Processor";
import ProtocolId3DigitProcessor from "./Preprocessor/Client/ProtocolId-3Digit-Processor";
import ProtocolIdProcessor from "./Preprocessor/Client/ProtocolIdProcessor";
import SourceEnumeratorProcessor from "./Preprocessor/Client/SourceEnumeratorProcessor";
import SourceSetPreprocessor from "./Preprocessor/Client/SourceSetPreprocessor";
import UCS2Preprocessor from "./Preprocessor/Client/UCS2Preprocessor";
import Preprocessor from "./Preprocessor/Preprocessor";
export default class ProcessorManager {
static preprocessors: PduProcessor[];
static postprocessors: PduProcessor[];
private static readonly logger: Logger = new Logger(this.name);
static preprocessors: PduProcessor[];
static postprocessors: PduProcessor[];
private static readonly logger: Logger = new Logger(this.name);
constructor() {
// This is an IDIOTIC solution, but it works
// Try running eb22a43 to find out what's wrong with the previous approach
ProcessorManager.postprocessors = [
new EnquireLinkReplyProcessor(Center.name),
new DeliverSmReplyProcessor(Client.name),
new SubmitSmReplyProcessor(Center.name),
new BindTranscieverReplyProcessor(Center.name),
new EchoPduProcessor(Center.name),
new DeliveryReceiptProcessor(Center.name)
];
ProcessorManager.preprocessors = [
new DestinationEnumeratorProcessor(Client.name),
new SourceEnumeratorProcessor(Client.name),
new DestinationEnumeratorProcessor(Center.name),
new SourceEnumeratorProcessor(Center.name),
new DeliveryReceiptRequestProcessor(Client.name),
new LongSmsProcessor(Client.name)
];
}
constructor() {
// This is an IDIOTIC solution, but it works
// Try running eb22a43 to find out what's wrong with the previous approach
ProcessorManager.postprocessors = [
new EnquireLinkReplyProcessor(Center.name),
new DeliverSmReplyProcessor(Client.name),
new SubmitSmReplyProcessor(Center.name),
new BindTranscieverReplyProcessor(Center.name),
new EchoPduProcessor(Center.name),
new DeliveryReceiptProcessor(Center.name)
];
ProcessorManager.preprocessors = [
new DestinationEnumeratorProcessor(Client.name),
new SourceEnumeratorProcessor(Client.name),
new DestinationEnumeratorProcessor(Center.name),
new SourceEnumeratorProcessor(Center.name),
new DeliveryReceiptRequestProcessor(Client.name),
new LongSmsProcessor(Client.name),
new LongSmsProcessor(Center.name),
new ProtocolIdProcessor(Client.name),
new ProtocolIdProcessor(Center.name),
new UCS2Preprocessor(Client.name),
new UCS2Preprocessor(Center.name),
new ProtocolId2DigitProcessor(Client.name),
new ProtocolId2DigitProcessor(Center.name),
new ProtocolId3DigitProcessor(Client.name),
new ProtocolId3DigitProcessor(Center.name),
new SourceSetPreprocessor(Client.name),
new SourceSetPreprocessor(Center.name),
new DestinationSetPreprocessor(Client.name),
new DestinationSetPreprocessor(Center.name),
new GSM0338Preprocessor(Client.name),
new GSM0338Preprocessor(Center.name)
];
}
static get processors(): PduProcessor[] {
return this.preprocessors.concat(this.postprocessors);
}
static get processors(): PduProcessor[] {
return this.preprocessors.concat(this.postprocessors);
}
static getProcessors(name: string): PduProcessor[] {
this.logger.log1(`Looking for processor with name ${name}...`);
let pduProcessors: PduProcessor[] = this.processors.filter((processor: PduProcessor) => processor.name === name);
this.logger.log1(`Found ${pduProcessors.length} processor(s) with name ${name}`);
return pduProcessors;
}
static getProcessors(name: string): PduProcessor[] {
this.logger.log1(`Looking for processor with name ${name}...`);
let pduProcessors: PduProcessor[] = this.processors.filter((processor: PduProcessor) => processor.name === name);
this.logger.log1(`Found ${pduProcessors.length} processor(s) with name ${name}`);
return pduProcessors;
}
static attachProcessors(session: SmppSession, processors: PduProcessor[]): void {
this.logger.log1(`Trying to attach processor ${processors.toString()} to session ${session.constructor.name}-${session.id}`);
for (const processor of processors) {
if (this.areCompatible(session, processor)) {
// This could be done a little better but this is OK for now
switch (processor.type) {
case Preprocessor.name:
session.attachPreprocessor(processor);
break;
case Postprocessor.name:
session.attachPostprocessor(processor);
break;
default:
this.logger.log1(`Processor ${processor.name} is not a preprocessor or a postprocessor`);
break;
}
}
}
}
static attachProcessors(session: SmppSession, processors: PduProcessor[]): void {
this.logger.log1(`Trying to attach processor ${processors.toString()} to session ${session.constructor.name}-${session.id}`);
for (const processor of processors) {
if (this.areCompatible(session, processor)) {
// This could be done a little better but this is OK for now
switch (processor.type) {
case Preprocessor.name:
session.attachPreprocessor(processor);
break;
case Postprocessor.name:
session.attachPostprocessor(processor);
break;
default:
this.logger.log1(`Processor ${processor.name} is not a preprocessor or a postprocessor`);
break;
}
}
}
}
static detachProcessors(session: SmppSession, processors: PduProcessor[]): void {
this.logger.log1(`Trying to detach processors ${processors.toString()} from session ${session.constructor.name}-${session.id}`);
for (const processor of processors) {
switch (processor.type) {
case Preprocessor.name:
session.detachPreprocessor(processor);
break;
case Postprocessor.name:
session.detachPostprocessor(processor);
break;
default:
this.logger.log1(`Processor ${processor.name} is not a preprocessor or a postprocessor`);
break;
}
}
}
static detachProcessors(session: SmppSession, processors: PduProcessor[]): void {
this.logger.log1(`Trying to detach processors ${processors.toString()} from session ${session.constructor.name}-${session.id}`);
for (const processor of processors) {
switch (processor.type) {
case Preprocessor.name:
session.detachPreprocessor(processor);
break;
case Postprocessor.name:
session.detachPostprocessor(processor);
break;
default:
this.logger.log1(`Processor ${processor.name} is not a preprocessor or a postprocessor`);
break;
}
}
}
static areCompatible(session: SmppSession, processor: PduProcessor): boolean {
this.logger.log1(`Checking compatibility between session ${session.constructor.name}-${session.id} and processor ${processor.name}`);
return session.constructor.name === processor.sessionType;
}
static areCompatible(session: SmppSession, processor: PduProcessor): boolean {
this.logger.log1(`Checking compatibility between session ${session.constructor.name}-${session.id} and processor ${processor.name}`);
return session.constructor.name === processor.sessionType;
}
static getProcessorsForType(type: string): PduProcessor[] {
return this.processors.filter((processor: PduProcessor) => processor.sessionType === type);
}
static getProcessorsForType(type: string): PduProcessor[] {
return this.processors.filter((processor: PduProcessor) => processor.sessionType === type);
}
static getPreprocessorsForType(type: string): PduProcessor[] {
return this.preprocessors.filter((processor: PduProcessor) => processor.sessionType === type);
}
static getPreprocessorsForType(type: string): PduProcessor[] {
return this.preprocessors.filter((processor: PduProcessor) => processor.sessionType === type);
}
static getPostprocessorsForType(type: string): PduProcessor[] {
return this.postprocessors.filter((processor: PduProcessor) => processor.sessionType === type);
}
static getPostprocessorsForType(type: string): PduProcessor[] {
return this.postprocessors.filter((processor: PduProcessor) => processor.sessionType === type);
}
}

View File

@@ -1,29 +1,29 @@
export default class PersistentPromise {
private readonly promise: Promise<any>;
private promiseResolve: ((value?: any) => void) | undefined;
private promiseReject: ((reason?: any) => void) | undefined;
private readonly promise: Promise<any>;
private promiseResolve: ((value?: any) => void) | undefined;
private promiseReject: ((reason?: any) => void) | undefined;
constructor(callback: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void) {
this.promise = new Promise((resolve, reject) => {
this.promiseResolve = resolve;
this.promiseReject = reject;
callback(resolve, reject);
});
}
constructor(callback: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void) {
this.promise = new Promise((resolve, reject) => {
this.promiseResolve = resolve;
this.promiseReject = reject;
callback(resolve, reject);
});
}
resolve(value?: any): void {
if (this.promiseResolve) {
this.promiseResolve(value);
}
}
resolve(value?: any): void {
if (this.promiseResolve) {
this.promiseResolve(value);
}
}
reject(reason?: any): void {
if (this.promiseReject) {
this.promiseReject(reason);
}
}
reject(reason?: any): void {
if (this.promiseReject) {
this.promiseReject(reason);
}
}
then(onfulfilled?: ((value: any) => any) | undefined | null, onrejected?: ((reason: any) => any) | undefined | null): Promise<any> {
return this.promise.then(onfulfilled, onrejected);
}
then(onfulfilled?: ((value: any) => any) | undefined | null, onrejected?: ((reason: any) => any) | undefined | null): Promise<any> {
return this.promise.then(onfulfilled, onrejected);
}
}

View File

@@ -7,138 +7,138 @@ import ProcessorManager from "./PDUProcessor/ProcessorManager";
import SmppSession from "./SmppSession";
export default abstract class SessionManager {
// I could've done this by passing these abstract properties to the constructor, but I wanted to have the possibility
// of implementing additional methods
abstract comparatorFn: (arg: any, session: SmppSession) => boolean;
readonly abstract identifier: string;
readonly abstract ManagedSessionClass: any;
readonly abstract StorageFile: string;
readonly SESSION_ADDED_EVENT: string = "SESSION ADDED";
// I could've done this by passing these abstract properties to the constructor, but I wanted to have the possibility
// of implementing additional methods
abstract comparatorFn: (arg: any, session: SmppSession) => boolean;
readonly abstract identifier: string;
readonly abstract ManagedSessionClass: any;
readonly abstract StorageFile: string;
readonly SESSION_ADDED_EVENT: string = "SESSION ADDED";
sessions: SmppSession[] = [];
sessionId: number = 0;
readonly logger: Logger = new Logger("SessionManager");
readonly eventEmitter: EventEmitter = new EventEmitter();
sessions: SmppSession[] = [];
sessionId: number = 0;
readonly logger: Logger = new Logger("SessionManager");
readonly eventEmitter: EventEmitter = new EventEmitter();
addSession(session: SmppSession): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.logger.log1(`Adding session with id ${session.id}`);
this.sessions.push(session);
this.eventEmitter.emit(this.SESSION_ADDED_EVENT, session.id);
resolve();
});
}
addSession(session: SmppSession): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.logger.log1(`Adding session with id ${session.id}`);
this.sessions.push(session);
this.eventEmitter.emit(this.SESSION_ADDED_EVENT, session.id);
resolve();
});
}
on(event: string, listener: (...args: any[]) => void): void {
this.eventEmitter.on(event, listener);
}
on(event: string, listener: (...args: any[]) => void): void {
this.eventEmitter.on(event, listener);
}
getSessions(): Promise<SmppSession[]> {
return Promise.resolve(this.sessions);
}
getSessions(): Promise<SmppSession[]> {
return Promise.resolve(this.sessions);
}
removeSession(session: SmppSession): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.logger.log1(`Removing session with id ${session.id}`);
session.close();
session.destroy();
this.sessions = this.sessions.filter(s => s.id !== session.id);
resolve();
});
}
removeSession(session: SmppSession): Promise<void> {
return new Promise<void>((resolve, reject) => {
this.logger.log1(`Removing session with id ${session.id}`);
session.close();
session.destroy();
this.sessions = this.sessions.filter(s => s.id !== session.id);
resolve();
});
}
getSession(id: number): Promise<SmppSession> {
return new Promise<SmppSession>((resolve, reject) => {
this.logger.log1(`Looking for session with id ${id}...`);
let session: SmppSession | undefined = this.sessions.find(s => s.id == id);
if (session) {
this.logger.log1(`Found session with id ${id}`);
resolve(session);
} else {
this.logger.log1(`Session with id ${id} not found`);
reject(`Session with id ${id} not found`);
}
});
}
getSession(id: number): Promise<SmppSession> {
return new Promise<SmppSession>((resolve, reject) => {
this.logger.log1(`Looking for session with id ${id}...`);
let session: SmppSession | undefined = this.sessions.find(s => s.id == id);
if (session) {
this.logger.log1(`Found session with id ${id}`);
resolve(session);
} else {
this.logger.log1(`Session with id ${id} not found`);
reject(`Session with id ${id} not found`);
}
});
}
setup(): void {
try {
this.logger.log1(`Loading ${this.ManagedSessionClass.name} from ${this.StorageFile}`)
let sessions: Buffer = fs.readFileSync(this.StorageFile);
let loadedSessions: any[] = JSON.parse(String(sessions));
this.logger.log1(`Loaded ${loadedSessions.length} clients from ${this.StorageFile}`);
loadedSessions.forEach(session => {
this.createSession(session.url || session.port, session.username, session.password).then((sessionObj: SmppSession) => {
sessionObj.defaultSingleJob = Job.deserialize(session.defaultSingleJob);
sessionObj.defaultMultipleJob = Job.deserialize(session.defaultMultipleJob);
setup(): void {
try {
this.logger.log1(`Loading ${this.ManagedSessionClass.name} from ${this.StorageFile}`)
let sessions: Buffer = fs.readFileSync(this.StorageFile);
let loadedSessions: any[] = JSON.parse(String(sessions));
this.logger.log1(`Loaded ${loadedSessions.length} clients from ${this.StorageFile}`);
loadedSessions.forEach(session => {
this.createSession(session.url || session.port, session.username, session.password).then((sessionObj: SmppSession) => {
sessionObj.defaultSingleJob = Job.deserialize(session.defaultSingleJob);
sessionObj.defaultMultipleJob = Job.deserialize(session.defaultMultipleJob);
let loadedProcessors: PduProcessor[] = session.preprocessors.concat(session.postprocessors);
sessionObj.appliedProcessors.forEach((processor: PduProcessor) => {
let processorsToDetach: PduProcessor[] = loadedProcessors.filter(p => p.name === processor.name);
ProcessorManager.detachProcessors(sessionObj, processorsToDetach);
});
loadedProcessors.forEach((processor: PduProcessor) => {
if (!sessionObj.appliedProcessors.find(p => p.name === processor.name)) {
ProcessorManager.attachProcessors(sessionObj, ProcessorManager.getProcessors(processor.name));
}
});
});
});
} catch (e) {
this.logger.log1(`Error loading centers from ${this.StorageFile}: ${e}`);
return;
}
}
let loadedProcessors: PduProcessor[] = session.preprocessors.concat(session.postprocessors);
sessionObj.appliedProcessors.forEach((processor: PduProcessor) => {
let processorsToDetach: PduProcessor[] = loadedProcessors.filter(p => p.name === processor.name);
ProcessorManager.detachProcessors(sessionObj, processorsToDetach);
});
loadedProcessors.forEach((processor: PduProcessor) => {
if (!sessionObj.appliedProcessors.find(p => p.name === processor.name)) {
ProcessorManager.attachProcessors(sessionObj, ProcessorManager.getProcessors(processor.name));
}
});
});
});
} catch (e) {
this.logger.log1(`Error loading centers from ${this.StorageFile}: ${e}`);
return;
}
}
createSession(arg: any, username: string, password: string): Promise<SmppSession> {
return new Promise<SmppSession>((resolve, reject) => {
this.logger.log1(`Creating session of type ${this.ManagedSessionClass.name} with arg ${arg}`);
this.getExisting(arg).then((s: SmppSession) => {
resolve(s);
}, err => {
});
this.verifyField(arg, reject);
username = username || "";
password = password || "";
createSession(arg: any, username: string, password: string): Promise<SmppSession> {
return new Promise<SmppSession>((resolve, reject) => {
this.logger.log1(`Creating session of type ${this.ManagedSessionClass.name} with arg ${arg}`);
this.getExisting(arg).then((s: SmppSession) => {
resolve(s);
}, err => {
});
this.verifyField(arg, reject);
username = username || "";
password = password || "";
let session = new this.ManagedSessionClass(this.sessionId++, arg, username, password);
this.addSession(session).then(() => {
resolve(session);
});
});
}
let session = new this.ManagedSessionClass(this.sessionId++, arg, username, password);
this.addSession(session).then(() => {
resolve(session);
});
});
}
verifyField(field: string, reject: (reason?: any) => void) {
if (!field) {
let error = `Request to make a new session failed because of missing ${field}.`;
this.logger.log1(error);
reject(error);
}
}
verifyField(field: string, reject: (reason?: any) => void) {
if (!field) {
let error = `Request to make a new session failed because of missing ${field}.`;
this.logger.log1(error);
reject(error);
}
}
cleanup(): void {
this.logger.log1(`Saving centers to ${this.StorageFile}...`);
fs.writeFileSync(this.StorageFile, JSON.stringify(this.serialize(), null, 4));
}
cleanup(): void {
this.logger.log1(`Saving centers to ${this.StorageFile}...`);
fs.writeFileSync(this.StorageFile, JSON.stringify(this.serialize(), null, 4));
}
serialize(): object {
this.logger.log1(`Serializing ${this.sessions.length} clients`);
return this.sessions.map((session: SmppSession) => {
return session.serialize();
});
}
serialize(): object {
this.logger.log1(`Serializing ${this.sessions.length} clients`);
return this.sessions.map((session: SmppSession) => {
return session.serialize();
});
}
getExisting(arg: any): Promise<SmppSession> {
return new Promise<SmppSession>((resolve, reject) => {
this.logger.log1(`Looking for session with arg ${arg}...`);
let session: SmppSession | undefined = this.sessions.find(this.comparatorFn.bind(this, arg));
if (session) {
this.logger.log1(`Found session with arg ${arg}`);
resolve(session);
} else {
this.logger.log1(`Session with arg ${arg} not found`);
reject(`Session with arg ${arg} not found`);
}
});
}
getExisting(arg: any): Promise<SmppSession> {
return new Promise<SmppSession>((resolve, reject) => {
this.logger.log1(`Looking for session with arg ${arg}...`);
let session: SmppSession | undefined = this.sessions.find(this.comparatorFn.bind(this, arg));
if (session) {
this.logger.log1(`Found session with arg ${arg}`);
resolve(session);
} else {
this.logger.log1(`Session with arg ${arg} not found`);
reject(`Session with arg ${arg} not found`);
}
});
}
}

View File

@@ -12,235 +12,235 @@ const NanoTimer = require("nanotimer");
const smpp = require("smpp");
export default abstract class SmppSession {
readonly EVENT: any = {
STATUS_CHANGED: "STATUS_CHANGED",
STATE_CHANGED: "STATE_CHANGED",
ANY_PDU_TX: "ANY_PDU_TX",
ANY_PDU_RX: "ANY_PDU_RX",
MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT",
};
abstract STATUSES: string[];
readonly EVENT: any = {
STATUS_CHANGED: "STATUS_CHANGED",
STATE_CHANGED: "STATE_CHANGED",
ANY_PDU_TX: "ANY_PDU_TX",
ANY_PDU_RX: "ANY_PDU_RX",
MESSAGE_SEND_COUNTER_UPDATE_EVENT: "MESSAGE_SEND_COUNTER_UPDATE_EVENT",
};
abstract STATUSES: string[];
processors: { [key: string]: PduProcessor[] } = {};
readonly UPDATE_WS: string = "UPDATE_WS";
readonly eventEmitter: EventEmitter = new EventEmitter();
readonly logger: Logger = new Logger(this.constructor.name);
readonly sendTimer: any = new NanoTimer();
readonly counterUpdateTimer: any = new NanoTimer();
readonly MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500;
processors: { [key: string]: PduProcessor[] } = {};
readonly UPDATE_WS: string = "UPDATE_WS";
readonly eventEmitter: EventEmitter = new EventEmitter();
readonly logger: Logger = new Logger(this.constructor.name);
readonly sendTimer: any = new NanoTimer();
readonly counterUpdateTimer: any = new NanoTimer();
readonly MESSAGE_SEND_UPDATE_DELAY: number = Number(process.env.MESSAGE_SEND_UPDATE_DELAY) || 500;
protected constructor() {
this.eventEmitter.on(this.EVENT.STATE_CHANGED, () => this.updateWs(this.EVENT.STATE_CHANGED));
this.eventEmitter.on(this.EVENT.STATUS_CHANGED, () => this.updateWs(this.EVENT.STATUS_CHANGED));
this.eventEmitter.on(this.EVENT.ANY_PDU_TX, (pdu: any) => this.updateWs(this.EVENT.ANY_PDU_TX, [pdu]));
this.eventEmitter.on(this.EVENT.ANY_PDU_RX, (pdu: any) => this.updateWs(this.EVENT.ANY_PDU_RX, [pdu]));
this.eventEmitter.on(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count]));
protected constructor() {
this.eventEmitter.on(this.EVENT.STATE_CHANGED, () => this.updateWs(this.EVENT.STATE_CHANGED));
this.eventEmitter.on(this.EVENT.STATUS_CHANGED, () => this.updateWs(this.EVENT.STATUS_CHANGED));
this.eventEmitter.on(this.EVENT.ANY_PDU_TX, (pdu: any) => this.updateWs(this.EVENT.ANY_PDU_TX, [pdu]));
this.eventEmitter.on(this.EVENT.ANY_PDU_RX, (pdu: any) => this.updateWs(this.EVENT.ANY_PDU_RX, [pdu]));
this.eventEmitter.on(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, (count: number) => this.updateWs(this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT, [count]));
this.processors[Preprocessor.name] = [];
this.processors[Postprocessor.name] = [];
}
this.processors[Preprocessor.name] = [];
this.processors[Postprocessor.name] = [];
}
get appliedProcessors(): PduProcessor[] {
return this.processors[Preprocessor.name].concat(this.processors[Postprocessor.name]);
}
get appliedProcessors(): PduProcessor[] {
return this.processors[Preprocessor.name].concat(this.processors[Postprocessor.name]);
}
abstract _username: string;
abstract _username: string;
get username(): string {
return this._username;
}
get username(): string {
return this._username;
}
set username(username: string) {
this._username = username;
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
set username(username: string) {
this._username = username;
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
abstract _password: string;
abstract _password: string;
get password(): string {
return this._password;
}
get password(): string {
return this._password;
}
set password(password: string) {
this._password = password;
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
set password(password: string) {
this._password = password;
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
abstract _id: number;
abstract _id: number;
get id(): number {
return this._id;
}
get id(): number {
return this._id;
}
abstract _status: string;
abstract _status: string;
get status(): string {
return this._status;
}
get status(): string {
return this._status;
}
set status(status: string) {
this._status = status;
this.eventEmitter.emit(this.EVENT.STATUS_CHANGED, this.status);
}
set status(status: string) {
this._status = status;
this.eventEmitter.emit(this.EVENT.STATUS_CHANGED, this.status);
}
abstract _defaultSingleJob: Job;
abstract _defaultSingleJob: Job;
get defaultSingleJob(): Job {
return this._defaultSingleJob;
}
get defaultSingleJob(): Job {
return this._defaultSingleJob;
}
set defaultSingleJob(job: Job) {
this._defaultSingleJob = job;
job.on(Job.STATE_CHANGED, this.eventJobUpdated);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
set defaultSingleJob(job: Job) {
this._defaultSingleJob = job;
job.on(Job.STATE_CHANGED, this.eventJobUpdated);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
abstract _defaultMultipleJob: Job;
abstract _defaultMultipleJob: Job;
get defaultMultipleJob(): Job {
return this._defaultMultipleJob;
}
get defaultMultipleJob(): Job {
return this._defaultMultipleJob;
}
set defaultMultipleJob(job: Job) {
this._defaultMultipleJob = job;
job.on(Job.STATE_CHANGED, this.eventJobUpdated);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
set defaultMultipleJob(job: Job) {
this._defaultMultipleJob = job;
job.on(Job.STATE_CHANGED, this.eventJobUpdated);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
setStatus(statusIndex: number) {
this._status = this.STATUSES[statusIndex];
this.eventEmitter.emit(this.EVENT.STATUS_CHANGED, this.status);
}
setStatus(statusIndex: number) {
this._status = this.STATUSES[statusIndex];
this.eventEmitter.emit(this.EVENT.STATUS_CHANGED, this.status);
}
abstract sendPdu(pdu: object, force?: boolean): Promise<object>;
abstract sendPdu(pdu: object, force?: boolean): Promise<object>;
doSendPdu(pdu: PDU, session: any): Promise<any> {
return new Promise<any>((resolve, reject) => {
let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
let maxMessageLength: number = LongSmsProcessor.maxMessageSizeBits / characterSizeBits;
if (!!pdu.short_message && pdu.short_message.length > maxMessageLength) {
pdu.short_message = pdu.short_message.substring(0, maxMessageLength);
}
session.send(pdu, (reply: any) => resolve(reply));
this.eventEmitter.emit(this.EVENT.ANY_PDU_TX, pdu);
});
}
doSendPdu(pdu: PDU, session: any): Promise<any> {
return new Promise<any>((resolve, reject) => {
let characterSizeBits: number = LongSmsProcessor.getCharacterSizeForEncoding(pdu);
let maxMessageLength: number = LongSmsProcessor.maxMessageSizeBits / characterSizeBits;
if (!!pdu.short_message && pdu.short_message.length > maxMessageLength) {
pdu.short_message = pdu.short_message.substring(0, maxMessageLength);
}
session.send(pdu, (reply: any) => resolve(reply));
this.eventEmitter.emit(this.EVENT.ANY_PDU_TX, pdu);
});
}
abstract destroy(): void;
abstract destroy(): void;
sendSingle(job: Job): Promise<object> {
return this.sendPdu(job.pdu);
}
sendSingle(job: Job): Promise<object> {
return this.sendPdu(job.pdu);
}
sendSingleDefault(): Promise<object> {
return this.sendSingle(this.defaultSingleJob);
}
sendSingleDefault(): Promise<object> {
return this.sendSingle(this.defaultSingleJob);
}
abstract sendMultiple(job: Job): Promise<void>;
abstract sendMultiple(job: Job): Promise<void>;
sendMultipleDefault(): Promise<void> {
return this.sendMultiple(this.defaultMultipleJob);
}
sendMultipleDefault(): Promise<void> {
return this.sendMultiple(this.defaultMultipleJob);
}
cancelSendInterval(): void {
this.sendTimer.clearInterval();
this.counterUpdateTimer.clearInterval();
this.setStatus(this.STATUSES.length - 2);
}
cancelSendInterval(): void {
this.sendTimer.clearInterval();
this.counterUpdateTimer.clearInterval();
this.setStatus(this.STATUSES.length - 2);
}
abstract close(): Promise<void>;
abstract close(): Promise<void>;
serialize(): object {
let obj = {
id: this._id,
username: this._username,
password: this._password,
status: this._status,
defaultSingleJob: this._defaultSingleJob.serialize(),
defaultMultipleJob: this._defaultMultipleJob.serialize(),
preprocessors: this.processors.Preprocessor.map((p: PduProcessor) => p.serialize()),
postprocessors: this.processors.Postprocessor.map((p: PduProcessor) => p.serialize()),
availablePreprocessors: ProcessorManager.getPreprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
availablePostprocessors: ProcessorManager.getPostprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
};
return this.postSerialize(obj);
}
serialize(): object {
let obj = {
id: this._id,
username: this._username,
password: this._password,
status: this._status,
defaultSingleJob: this._defaultSingleJob.serialize(),
defaultMultipleJob: this._defaultMultipleJob.serialize(),
preprocessors: this.processors.Preprocessor.map((p: PduProcessor) => p.serialize()),
postprocessors: this.processors.Postprocessor.map((p: PduProcessor) => p.serialize()),
availablePreprocessors: ProcessorManager.getPreprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
availablePostprocessors: ProcessorManager.getPostprocessorsForType(this.constructor.name).map((p: PduProcessor) => p.serialize()),
};
return this.postSerialize(obj);
}
abstract postSerialize(obj: object): object;
abstract postSerialize(obj: object): object;
on(event: string, callback: (...args: any[]) => void): void {
this.eventEmitter.on(event, callback);
}
on(event: string, callback: (...args: any[]) => void): void {
this.eventEmitter.on(event, callback);
}
updateWs(event: string, args?: any[]): void {
this.logger.log1(`Update WS: ${event}`);
let message: WSMessage = {
type: event,
identifier: `${this.constructor.name}:${this.id.toString()}`
};
switch (event) {
case this.EVENT.STATE_CHANGED:
message.data = this.serialize();
break;
case this.EVENT.STATUS_CHANGED:
message.data = this.status;
break;
case this.EVENT.ANY_PDU_RX:
case this.EVENT.ANY_PDU_TX:
message.data = args![0];
break;
case this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT:
message.data = args![0];
break;
}
this.eventEmitter.emit(this.UPDATE_WS, message);
}
updateWs(event: string, args?: any[]): void {
this.logger.log1(`Update WS: ${event}`);
let message: WSMessage = {
type: event,
identifier: `${this.constructor.name}:${this.id.toString()}`
};
switch (event) {
case this.EVENT.STATE_CHANGED:
message.data = this.serialize();
break;
case this.EVENT.STATUS_CHANGED:
message.data = this.status;
break;
case this.EVENT.ANY_PDU_RX:
case this.EVENT.ANY_PDU_TX:
message.data = args![0];
break;
case this.EVENT.MESSAGE_SEND_COUNTER_UPDATE_EVENT:
message.data = args![0];
break;
}
this.eventEmitter.emit(this.UPDATE_WS, message);
}
eventJobUpdated(): void {
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
eventJobUpdated(): void {
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
attachPreprocessor(processor: PduProcessor): void {
this.attachProcessor(processor, this.processors.Preprocessor);
}
attachPreprocessor(processor: PduProcessor): void {
this.attachProcessor(processor, this.processors.Preprocessor);
}
attachPostprocessor(processor: PduProcessor): void {
this.attachProcessor(processor, this.processors.Postprocessor);
}
attachPostprocessor(processor: PduProcessor): void {
this.attachProcessor(processor, this.processors.Postprocessor);
}
detachPreprocessor(processor: PduProcessor): void {
this.detachProcessor(processor, this.processors.Preprocessor);
}
detachPreprocessor(processor: PduProcessor): void {
this.detachProcessor(processor, this.processors.Preprocessor);
}
detachPostprocessor(processor: PduProcessor): void {
this.detachProcessor(processor, this.processors.Postprocessor);
}
detachPostprocessor(processor: PduProcessor): void {
this.detachProcessor(processor, this.processors.Postprocessor);
}
eventAnyPdu(session: any, pdu: PDU): Promise<any> {
if (!!pdu) {
this.eventEmitter.emit(this.EVENT.ANY_PDU_RX, pdu);
// console.log("IS PDU TIME");
this.logger.log6(pdu);
this.processors.Postprocessor.forEach((processor: PduProcessor) => processor.processPdu(session, pdu, this));
}
return Promise.resolve();
}
eventAnyPdu(session: any, pdu: PDU): Promise<any> {
if (!!pdu) {
this.eventEmitter.emit(this.EVENT.ANY_PDU_RX, pdu);
// console.log("IS PDU TIME");
this.logger.log6(pdu);
this.processors.Postprocessor.forEach((processor: PduProcessor) => processor.processPdu(session, pdu, this));
}
return Promise.resolve();
}
private detachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
if (array.indexOf(processor) >= 0) {
array.splice(array.indexOf(processor), 1);
this.logger.log1(`Detaching PDU processor: ${processor.constructor.name}-${this.id}, now active: ${array.length} processors`);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
}
private detachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
if (array.indexOf(processor) >= 0) {
array.splice(array.indexOf(processor), 1);
this.logger.log1(`Detaching PDU processor: ${processor.constructor.name}-${this.id}, now active: ${array.length} processors`);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
}
}
private attachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
if (array.indexOf(processor) === -1) {
array.push(processor);
this.logger.log1(`Attaching PDU processor: ${processor.constructor.name}-${this.id}, now active: ${array.length} processors`);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
} else {
this.logger.log1(`PDU processor: ${processor.constructor.name}-${this.id} already attached to session`);
}
}
private attachProcessor(processor: PduProcessor, array: PduProcessor[]): void {
if (array.indexOf(processor) === -1) {
array.push(processor);
this.logger.log1(`Attaching PDU processor: ${processor.constructor.name}-${this.id}, now active: ${array.length} processors`);
this.eventEmitter.emit(this.EVENT.STATE_CHANGED, this.serialize());
} else {
this.logger.log1(`PDU processor: ${processor.constructor.name}-${this.id} already attached to session`);
}
}
}

View File

@@ -5,70 +5,70 @@ import SmppSession from "../SmppSession";
import ZlibCoder from "../ZlibCoder";
export default class ClientSet {
identifier: string;
private clients: any[];
private readonly type: string;
private readonly sessionId: number;
private readonly logger: Logger;
private readonly relevantSessionManager: SessionManager | undefined;
identifier: string;
private clients: any[];
private readonly type: string;
private readonly sessionId: number;
private readonly logger: Logger;
private readonly relevantSessionManager: SessionManager | undefined;
constructor(identifier: string, sessionManagers: SessionManager[]) {
this.clients = [];
this.identifier = identifier;
constructor(identifier: string, sessionManagers: SessionManager[]) {
this.clients = [];
this.identifier = identifier;
let data: string[] = identifier.split(':');
this.type = data[0];
this.sessionId = parseInt(data[1]);
let data: string[] = identifier.split(':');
this.type = data[0];
this.sessionId = parseInt(data[1]);
this.logger = new Logger(`ClientSet-${this.type}-${this.sessionId}`);
this.logger.log1(`Created client set for ${this.type} ${this.sessionId}`);
this.logger = new Logger(`ClientSet-${this.type}-${this.sessionId}`);
this.logger.log1(`Created client set for ${this.type} ${this.sessionId}`);
this.relevantSessionManager = sessionManagers.find(sm => sm.identifier === this.type);
if (!this.relevantSessionManager) {
this.logger.log1(`No session manager found for type ${this.type}`);
return;
}
if (this.relevantSessionManager) {
this.relevantSessionManager.getSessions().then((sessions) => {
sessions.forEach((session) => {
this.attachListener(session);
});
});
}
this.relevantSessionManager.on(this.relevantSessionManager.SESSION_ADDED_EVENT, this.eventOnSessionAdded.bind(this));
}
this.relevantSessionManager = sessionManagers.find(sm => sm.identifier === this.type);
if (!this.relevantSessionManager) {
this.logger.log1(`No session manager found for type ${this.type}`);
return;
}
if (this.relevantSessionManager) {
this.relevantSessionManager.getSessions().then((sessions) => {
sessions.forEach((session) => {
this.attachListener(session);
});
});
}
this.relevantSessionManager.on(this.relevantSessionManager.SESSION_ADDED_EVENT, this.eventOnSessionAdded.bind(this));
}
eventOnSessionAdded(sessionId: number): void {
this.logger.log2(`Session added: ${sessionId}`);
this.relevantSessionManager?.getSession(sessionId).then((session) => {
this.attachListener(session);
})
}
eventOnSessionAdded(sessionId: number): void {
this.logger.log2(`Session added: ${sessionId}`);
this.relevantSessionManager?.getSession(sessionId).then((session) => {
this.attachListener(session);
})
}
add(ws: any): void {
this.logger.log2(`Added client`);
this.clients.push(ws);
ws.on('close', this.eventOnClose.bind(this, ws));
}
add(ws: any): void {
this.logger.log2(`Added client`);
this.clients.push(ws);
ws.on('close', this.eventOnClose.bind(this, ws));
}
eventOnClose(ws: any): void {
this.logger.log2(`Removed client`);
this.clients.splice(this.clients.indexOf(ws), 1);
}
eventOnClose(ws: any): void {
this.logger.log2(`Removed client`);
this.clients.splice(this.clients.indexOf(ws), 1);
}
notifyClients(message: WSMessage) {
if (message.identifier !== this.identifier) return;
let textMessage: string = JSON.stringify(message);
let compressedMessage = ZlibCoder.compress(textMessage);
if (this.clients.length > 0) {
this.logger.log2(`Notifying clients: ${message}`);
this.clients.forEach((ws) => {
ws.send(compressedMessage);
});
}
}
notifyClients(message: WSMessage) {
if (message.identifier !== this.identifier) return;
let textMessage: string = JSON.stringify(message);
let compressedMessage = ZlibCoder.compress(textMessage);
if (this.clients.length > 0) {
this.logger.log2(`Notifying clients: ${message}`);
this.clients.forEach((ws) => {
ws.send(compressedMessage);
});
}
}
private attachListener(session: SmppSession) {
session.on(session.UPDATE_WS, (message: WSMessage) => this.notifyClients(message));
}
private attachListener(session: SmppSession) {
session.on(session.UPDATE_WS, (message: WSMessage) => this.notifyClients(message));
}
}

View File

@@ -7,45 +7,45 @@ const WebSocket = require("ws");
const WS_SERVER_PORT: number = Number(process.env.WS_SERVER_PORT) || 8191;
export default class WSServer {
private readonly clients: ClientSet[];
private readonly unknownClients: any[];
private readonly server: any;
private readonly logger: Logger;
private readonly sessionManagers: SessionManager[];
private readonly clients: ClientSet[];
private readonly unknownClients: any[];
private readonly server: any;
private readonly logger: Logger;
private readonly sessionManagers: SessionManager[];
constructor(sessionManagers: SessionManager[]) {
this.clients = [];
this.unknownClients = [];
this.server = new WebSocket.Server({port: WS_SERVER_PORT});
this.sessionManagers = sessionManagers;
this.logger = new Logger("WSServer");
this.server.on('connection', this.eventOnConnection.bind(this));
this.logger.log1(`WSServer listening at ws://localhost:${WS_SERVER_PORT}`);
}
constructor(sessionManagers: SessionManager[]) {
this.clients = [];
this.unknownClients = [];
this.server = new WebSocket.Server({port: WS_SERVER_PORT});
this.sessionManagers = sessionManagers;
this.logger = new Logger("WSServer");
this.server.on('connection', this.eventOnConnection.bind(this));
this.logger.log1(`WSServer listening at ws://localhost:${WS_SERVER_PORT}`);
}
private eventOnConnection(ws: WebSocket): void {
this.logger.log1("New connection");
this.unknownClients.push(ws);
// @ts-ignore
ws.on('message', this.eventOnMessage.bind(this, ws));
// @ts-ignore
ws.on('close', this.eventOnClose.bind(this, ws));
}
private eventOnConnection(ws: WebSocket): void {
this.logger.log1("New connection");
this.unknownClients.push(ws);
// @ts-ignore
ws.on('message', this.eventOnMessage.bind(this, ws));
// @ts-ignore
ws.on('close', this.eventOnClose.bind(this, ws));
}
private eventOnMessage(ws: any, message: string): void {
this.logger.log1("New message");
message = String(message);
this.unknownClients.splice(this.unknownClients.indexOf(ws), 1);
let clientSet: ClientSet | undefined = this.clients.find((clientSet: ClientSet) => clientSet.identifier === message);
if (!clientSet) {
clientSet = new ClientSet(message, this.sessionManagers);
this.clients.push(clientSet);
}
clientSet.add(ws);
}
private eventOnMessage(ws: any, message: string): void {
this.logger.log1("New message");
message = String(message);
this.unknownClients.splice(this.unknownClients.indexOf(ws), 1);
let clientSet: ClientSet | undefined = this.clients.find((clientSet: ClientSet) => clientSet.identifier === message);
if (!clientSet) {
clientSet = new ClientSet(message, this.sessionManagers);
this.clients.push(clientSet);
}
clientSet.add(ws);
}
private eventOnClose(ws: any): void {
this.logger.log1("Connection closed");
this.unknownClients.splice(this.unknownClients.indexOf(ws), 1);
}
private eventOnClose(ws: any): void {
this.logger.log1("Connection closed");
this.unknownClients.splice(this.unknownClients.indexOf(ws), 1);
}
}

View File

@@ -1,11 +1,11 @@
const zlib = require("zlib");
export default class ZlibCoder {
static compress(input: string): Buffer {
return zlib.deflateSync(input).toString('base64');
}
static compress(input: string): Buffer {
return zlib.deflateSync(input).toString('base64');
}
static decompress(input: Buffer): string {
return zlib.decompress(input).toString();
}
static decompress(input: Buffer): string {
return zlib.decompress(input).toString();
}
}

25
src/main.js Normal file
View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var CenterSessionManager_1 = require("./Center/CenterSessionManager");
var ClientSessionManager_1 = require("./Client/ClientSessionManager");
var HttpServer_1 = require("./HttpServer/HttpServer");
var Logger_1 = require("./Logger");
var ProcessorManager_1 = require("./PDUProcessor/ProcessorManager");
var WSServer_1 = require("./WS/WSServer");
var PDU = require("smpp").PDU;
var logger = new Logger_1.default("main");
var pm = new ProcessorManager_1.default();
var clientManager = new ClientSessionManager_1.default();
var centerManager = new CenterSessionManager_1.default();
var wss = new WSServer_1.default([clientManager, centerManager]);
var httpServer = new HttpServer_1.default(clientManager, centerManager);
function cleanup() {
logger.log1("Cleaning up...");
clientManager.cleanup();
centerManager.cleanup();
process.exit(0);
}
process.on('exit', cleanup);
process.on('SIGINT', cleanup);
process.on('SIGUSR1', cleanup);
process.on('SIGUSR2', cleanup);

View File

@@ -17,10 +17,10 @@ let wss: WSServer = new WSServer([clientManager, centerManager]);
let httpServer: HttpServer = new HttpServer(clientManager, centerManager);
function cleanup(): void {
logger.log1("Cleaning up...");
clientManager.cleanup();
centerManager.cleanup();
process.exit(0);
logger.log1("Cleaning up...");
clientManager.cleanup();
centerManager.cleanup();
process.exit(0);
}
process.on('exit', cleanup);

View File

@@ -1,15 +1,15 @@
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"moduleResolution": "node",
"strict": false,
"verbatimModuleSyntax": false,
"noUncheckedIndexedAccess": false,
"moduleResolution": "NodeNext",
"module": "NodeNext",
"target": "ESNext",
"esModuleInterop": true,
"noImplicitAny": true,
"noEmit": true
},
"exclude": [
"./node_modules"
]
"include": ["src/**/*", "src/main.ts"]
}

13
tsup.config.ts Normal file
View File

@@ -0,0 +1,13 @@
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/main.ts'],
dts: false,
splitting: false,
sourcemap: false,
outDir: 'build',
clean: true,
minify: true,
target: ['esnext'],
format: ['cjs']
});

61
workfile.js Normal file
View File

@@ -0,0 +1,61 @@
import { connect } from "smpp";
import fs from "fs";
const optionsFile = "smpp_tester_options.json";
const exampleSession = {
url: "smpp://172.17.77.203:4445",
connection_count: 1,
system_id: "snt",
password: "snt",
source_addr: "44455",
destination_addr: "test123",
short_message: "SNT BENCHMARK!",
message_count: 100,
};
if (!fs.existsSync(optionsFile)) {
fs.writeFileSync(optionsFile, JSON.stringify([exampleSession], null, 4));
process.exit(1);
}
const options = fs.readFileSync(optionsFile, "utf8") ?? "[]";
const sessions = JSON.parse(options);
const smppSessions = [];
for (const session of sessions) {
for (let i = 0; i < session.connection_count; i++) {
const smppSession = connect(
{
url: session.url,
auto_enquire_link_period: 10000,
debug: true,
},
function () {
smppSession.bind_transceiver({
system_id: session.system_id,
password: session.password,
});
}
);
smppSessions.push({ properties: session, session: smppSession });
}
}
setTimeout(function () {
for (const session of smppSessions) {
for (let i = 0; i < session.properties.message_count; i++) {
session.session.submit_sm(
{
source_addr: session.properties.source_addr,
destination_addr: session.properties.destination_addr,
short_message: session.properties.short_message,
},
function (pdu) {
if (pdu.command_status === 0) {
console.log(pdu.message_id);
}
}
);
}
}
}, 1000);