• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

telefonicaid / iotagent-json / 6558328460

18 Oct 2023 08:34AM UTC coverage: 77.24%. Remained the same
6558328460

Pull #767

github

web-flow
Merge a29ef3d1e into 1346a3c11
Pull Request #767: remove placeholder from logs

364 of 540 branches covered (0.0%)

Branch coverage included in aggregate %.

20 of 20 new or added lines in 3 files covered. (100.0%)

834 of 1011 relevant lines covered (82.49%)

69.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.66
/lib/bindings/HTTPBinding.js
1
/*
2
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
3
 *
4
 * This file is part of iotagent-json
5
 *
6
 * iotagent-json is free software: you can redistribute it and/or
7
 * modify it under the terms of the GNU Affero General Public License as
8
 * published by the Free Software Foundation, either version 3 of the License,
9
 * or (at your option) any later version.
10
 *
11
 * iotagent-json is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 * See the GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public
17
 * License along with iotagent-json.
18
 * If not, seehttp://www.gnu.org/licenses/.
19
 *
20
 * For those usages not covered by the GNU Affero General Public License
21
 * please contact with::[contacto@tid.es]
22
 */
23

24
/* eslint-disable no-unused-vars */
25

26
const fs = require('fs');
1✔
27
const iotAgentLib = require('iotagent-node-lib');
1✔
28
const regenerateTransid = iotAgentLib.regenerateTransid;
1✔
29
const finishSouthBoundTransaction = iotAgentLib.finishSouthBoundTransaction;
1✔
30
const fillService = iotAgentLib.fillService;
1✔
31
const intoTrans = iotAgentLib.intoTrans;
1✔
32
const _ = require('underscore');
1✔
33
const commandHandler = require('../commandHandler');
1✔
34
const async = require('async');
1✔
35
const apply = async.apply;
1✔
36
const request = iotAgentLib.request;
1✔
37
const errors = require('../errors');
1✔
38
const express = require('express');
1✔
39
const iotaUtils = require('../iotaUtils');
1✔
40
const http = require('http');
1✔
41
const https = require('https');
1✔
42
const commonBindings = require('../commonBindings');
1✔
43
const bodyParser = require('body-parser');
1✔
44
const constants = require('../constants');
1✔
45
let context = {
1✔
46
    op: 'IOTAJSON.HTTP.Binding'
47
};
48
const config = require('../configService');
1✔
49
let httpBindingServer;
50
const transport = 'HTTP';
1✔
51

52
const { promisify } = require('util');
1✔
53
const json = promisify(bodyParser.json({ strict: false })); // accept anything JSON.parse accepts.
1✔
54
const text = promisify(bodyParser.text());
1✔
55
const raw = promisify(bodyParser.raw());
1✔
56

57
function parserBody() {
58
    // generic bodyParser
59
    return function (req, res, next) {
119✔
60
        if (req.is('text/plain')) {
6✔
61
            text(req, res).then(() => next(), next);
2✔
62
        } else if (req.is('application/octet-stream')) {
4✔
63
            raw(req, res).then(() => next(), next);
2✔
64
        } else {
65
            // req.is('json')
66
            json(req, res).then(() => next(), next);
2✔
67
        }
68
    };
69
}
70

71
function checkMandatoryParams(queryPayload) {
72
    return function (req, res, next) {
595✔
73
        const notFoundParams = [];
48✔
74
        let error;
75

76
        req.apiKey = req.query.k;
48✔
77
        req.deviceId = req.query.i;
48✔
78
        req.attr = req.params ? req.params.attrValue : undefined;
48!
79

80
        if (!req.apiKey) {
48!
81
            notFoundParams.push('API Key');
×
82
        }
83

84
        if (!req.deviceId) {
48!
85
            notFoundParams.push('Device Id');
×
86
        }
87

88
        // Check if retrievingParam
89
        if (queryPayload && !req.query.d && req.query.getCmd !== '1') {
48!
90
            notFoundParams.push('Payload');
×
91
        }
92
        if (req.method === 'POST' && !req.is('json') && !req.is('text/plain') && !req.is('application/octet-stream')) {
48!
93
            error = new errors.UnsupportedType('application/json, text/plain, application/octet-stream');
×
94
        }
95

96
        if (notFoundParams.length !== 0) {
48!
97
            next(new errors.MandatoryParamsNotFound(notFoundParams));
×
98
        } else {
99
            next(error);
48✔
100
        }
101
    };
102
}
103

104
function parseData(req, res, next) {
105
    let data;
106
    let error;
107
    let payload;
108

109
    if (req.body) {
13✔
110
        config.getLogger().debug(context, 'Using body %s', req.body);
12✔
111
        data = req.body;
12✔
112
        regenerateTransid(data);
12✔
113
    } else {
114
        payload = req.query.d;
1✔
115
        regenerateTransid(payload);
1✔
116
        config.getLogger().debug(context, 'Parsing payload %s', payload);
1✔
117

118
        try {
1✔
119
            if (payload) {
1!
120
                data = JSON.parse(payload);
×
121
            }
122
        } catch (e) {
123
            error = e;
×
124
        }
125
    }
126

127
    if (error) {
13!
128
        next(error);
×
129
    } else {
130
        req.jsonPayload = data;
13✔
131
        // This is just for log data
132
        if (req.body !== undefined) {
13✔
133
            data = data.toString('hex');
12✔
134
        }
135
        config.getLogger().debug(context, 'Parsed data: %j', data);
13✔
136
        next();
13✔
137
    }
138
}
139

140
function parseDataMultipleMeasure(req, res, next) {
141
    let data;
142
    let error;
143
    let payload;
144
    let dataArray;
145
    dataArray = [];
35✔
146
    if (req.body) {
35!
147
        config.getLogger().debug(context, 'Using body %s', req.body);
35✔
148
        if (!Array.isArray(req.body)) {
35✔
149
            dataArray.push(req.body);
29✔
150
        } else {
151
            dataArray = req.body;
6✔
152
        }
153
        regenerateTransid(dataArray);
35✔
154
    } else {
155
        payload = req.query.d;
×
156
        regenerateTransid(payload);
×
157
        config.getLogger().debug(context, 'Parsing payload %s', payload);
×
158
        try {
×
159
            if (payload) {
×
160
                data = JSON.parse(payload);
×
161
                dataArray.push(data);
×
162
            }
163
        } catch (e) {
164
            error = e;
×
165
        }
166
    }
167
    if (error) {
35!
168
        next(error);
×
169
    } else {
170
        req.jsonPayload = dataArray;
35✔
171
        config.getLogger().debug(context, 'Parsed data array: %j', dataArray);
35✔
172
        next();
35✔
173
    }
174
}
175

176
function executeCommand(apiKey, device, cmdName, serializedPayload, contentType, callback) {
177
    const options = {
6✔
178
        url: device.endpoint,
179
        method: 'POST',
180
        body: serializedPayload,
181
        headers: {
182
            'fiware-service': device.service,
183
            'fiware-servicepath': device.subservice,
184
            'content-type': contentType
185
        }
186
    };
187
    if (device.endpoint) {
6!
188
        // device.endpoint or another field like device.endpointExp ?
189
        const parser = iotAgentLib.dataPlugins.expressionTransformation;
6✔
190
        let attrList = iotAgentLib.dataPlugins.utils.getIdTypeServSubServiceFromDevice(device);
6✔
191
        attrList = device.staticAttributes ? attrList.concat(device.staticAttributes) : attrList.concat([]);
6!
192
        const ctxt = parser.extractContext(attrList, device);
6✔
193
        config.getLogger().debug(context, 'attrList %j for device %j', attrList, device);
6✔
194
        // expression result will be the full command payload
195
        let endpointRes = null;
6✔
196
        try {
6✔
197
            endpointRes = parser.applyExpression(device.endpoint, ctxt, device);
6✔
198
        } catch (e) {
199
            // no error should be reported
200
        }
201
        options.url = endpointRes ? endpointRes : device.endpoint;
6!
202
    }
203
    if (config.getConfig().http.timeout) {
6!
204
        options.timeout = config.getConfig().http.timeout;
×
205
    }
206
    config
6✔
207
        .getLogger()
208
        .debug(
209
            context,
210
            'Sending command %s with payload %s and with http options %j',
211
            cmdName,
212
            serializedPayload,
213
            options
214
        );
215
    request(options, function (error, response, body) {
6✔
216
        if (error || !response || (response.statusCode !== 200 && response.statusCode !== 201)) {
6!
217
            callback(new errors.HTTPCommandResponseError(response ? response.statusCode : 400, error));
×
218
        } else if (apiKey) {
6!
219
            config
6✔
220
                .getLogger()
221
                .info(
222
                    context,
223
                    'Cmd:\n %j was sent to the device %s with http options %j',
224
                    serializedPayload,
225
                    cmdName,
226
                    options
227
                );
228
            process.nextTick(commandHandler.updateCommand.bind(null, apiKey, device.id, device, body));
6✔
229
            callback();
6✔
230
        } else {
231
            config
×
232
                .getLogger()
233
                .info(
234
                    context,
235
                    'Cmd:\n %j was sent to the device %s with http options %j',
236
                    serializedPayload,
237
                    cmdName,
238
                    options
239
                );
240
            callback();
×
241
        }
242
    });
243
}
244

245
function addTimestamp(req, res, next) {
246
    const arr = req.jsonPayload;
43✔
247
    let timeStampData;
248
    for (const i in arr) {
43✔
249
        if (req.query.t && arr[i]) {
244✔
250
            timeStampData = arr[i];
4✔
251
            timeStampData[constants.TIMESTAMP_ATTRIBUTE] = req.query.t;
4✔
252
        }
253
    }
254
    next();
43✔
255
}
256

257
function handleIncomingMeasure(req, res, next) {
258
    let values;
259
    context = fillService(context, { service: 'n/a', subservice: 'n/a' });
43✔
260
    config
43✔
261
        .getLogger()
262
        .debug(context, 'Processing multiple HTTP measures for device %s with apiKey %s', req.deviceId, req.apiKey);
263

264
    function updateCommandHandler(error) {
265
        if (error) {
48!
266
            next(error);
×
267
            config.getLogger().error(
×
268
                context,
269
                /*jshint quotmark: double */
270
                "MEASURES-002: Couldn't send the updated values to the Context Broker due to an error: %j",
271
                /*jshint quotmark: single */
272
                error
273
            );
274
        } else {
275
            config
48✔
276
                .getLogger()
277
                .info(
278
                    context,
279
                    'Multiple measures for device %s with apiKey %s successfully updated',
280
                    req.deviceId,
281
                    req.apiKey
282
                );
283

284
            finishSouthBoundTransaction(next);
48✔
285
        }
286
    }
287

288
    function processHTTPWithDevice(device) {
289
        let payloadDataArr;
290
        let attributeArr;
291
        let attributeValues;
292
        attributeArr = [];
43✔
293
        payloadDataArr = [];
43✔
294

295
        if (req.attr && req.jsonPayload) {
43✔
296
            config.getLogger().debug(context, 'Parsing attr %s with value %s', req.attr, req.jsonPayload);
6✔
297
            req.jsonPayload = req.jsonPayload.toString('hex');
6✔
298
            const theAttr = [{ name: req.attr, value: req.jsonPayload, type: 'None' }];
6✔
299
            attributeArr.push(theAttr);
6✔
300
        } else {
301
            if (!Array.isArray(req.jsonPayload)) {
37✔
302
                payloadDataArr.push(req.jsonPayload);
2✔
303
            } else {
304
                payloadDataArr = req.jsonPayload;
35✔
305
            }
306
            if (req.jsonPayload) {
37✔
307
                for (const i in payloadDataArr) {
36✔
308
                    values = commonBindings.extractAttributes(device, payloadDataArr[i]);
42✔
309
                    attributeArr.push(values);
42✔
310
                }
311
            } else {
312
                attributeArr = [];
1✔
313
            }
314
        }
315

316
        if (attributeArr.length === 0) {
43✔
317
            finishSouthBoundTransaction(next);
1✔
318
        } else {
319
            for (const j in attributeArr) {
42✔
320
                attributeValues = attributeArr[j];
48✔
321
                config
48✔
322
                    .getLogger()
323
                    .debug(
324
                        context,
325
                        'Processing measure device %s with attributeArr %j attributeValues %j',
326
                        device.name,
327
                        attributeArr,
328
                        attributeValues
329
                    );
330
                if (req.isCommand) {
48✔
331
                    const executions = [];
1✔
332
                    for (const k in attributeValues) {
1✔
333
                        executions.push(
1✔
334
                            iotAgentLib.setCommandResult.bind(
335
                                null,
336
                                device.name,
337
                                config.getConfig().iota.defaultResource,
338
                                req.apiKey,
339
                                attributeValues[k].name,
340
                                attributeValues[k].value,
341
                                constants.COMMAND_STATUS_COMPLETED,
342
                                device
343
                            )
344
                        );
345
                    }
346
                    async.parallel(executions, updateCommandHandler);
1✔
347
                } else if (attributeValues.length > 0) {
47!
348
                    iotAgentLib.update(device.name, device.type, '', attributeValues, device, updateCommandHandler);
47✔
349
                } else {
350
                    finishSouthBoundTransaction(next);
×
351
                }
352
            }
353
        }
354
    }
355

356
    function processDeviceMeasure(error, device) {
357
        if (error) {
43!
358
            next(error);
×
359
        } else {
360
            const localContext = _.clone(context);
43✔
361
            req.device = device;
43✔
362
            localContext.service = device.service;
43✔
363
            localContext.subservice = device.subservice;
43✔
364
            intoTrans(localContext, processHTTPWithDevice)(device);
43✔
365
        }
366
    }
367

368
    iotaUtils.retrieveDevice(req.deviceId, req.apiKey, transport, processDeviceMeasure);
43✔
369
}
370

371
function isCommand(req, res, next) {
372
    if (
1!
373
        req.path ===
374
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_COMMANDS_PATH
1!
375
    ) {
376
        req.isCommand = true;
1✔
377
    }
378

379
    next();
1✔
380
}
381

382
function sendConfigurationToDevice(apiKey, deviceId, results, callback) {
383
    function handleDeviceResponse(innerCallback) {
384
        return function (error, response, body) {
4✔
385
            if (error) {
4!
386
                innerCallback(error);
×
387
            } else if (response && response.statusCode !== 200) {
4!
388
                innerCallback(new errors.DeviceEndpointError(response.statusCode, body));
×
389
            } else {
390
                innerCallback();
4✔
391
            }
392
        };
393
    }
394

395
    function sendRequest(device, results, innerCallback) {
396
        const resultRequest = {
4✔
397
            url: device.endpoint + constants.HTTP_CONFIGURATION_PATH,
398
            method: 'POST',
399
            json: iotaUtils.createConfigurationNotification(results),
400
            headers: {
401
                'fiware-service': device.service,
402
                'fiware-servicepath': device.subservice,
403
                'content-type': 'application/json'
404
            }
405
        };
406

407
        request(resultRequest, handleDeviceResponse(innerCallback));
4✔
408
    }
409
    iotaUtils.retrieveDevice(deviceId, apiKey, transport, function (error, device) {
4✔
410
        if (error) {
4!
411
            callback(error);
×
412
        } else if (!device.endpoint) {
4!
413
            callback(new errors.EndpointNotFound(device.id));
×
414
        } else {
415
            sendRequest(device, results, callback);
4✔
416
        }
417
    });
418
}
419

420
function handleConfigurationRequest(req, res, next) {
421
    function replyToDevice(error) {
422
        if (error) {
5!
423
            res.status(error.code).json(error);
×
424
        } else {
425
            res.status(200).json({});
5✔
426
        }
427
    }
428
    iotaUtils.retrieveDevice(req.deviceId, req.apiKey, transport, function (error, device) {
5✔
429
        if (error) {
5!
430
            next(error);
×
431
        } else {
432
            iotaUtils.manageConfiguration(
5✔
433
                req.apiKey,
434
                req.deviceId,
435
                device,
436
                req.jsonPayload,
437
                sendConfigurationToDevice,
438
                replyToDevice
439
            );
440
        }
441
    });
442
}
443

444
function handleError(error, req, res, next) {
445
    let code = 500;
×
446

447
    config.getLogger().debug(context, 'Error %s handling request: %s', error.name, error.message);
×
448

449
    if (error.code && String(error.code).match(/^[2345]\d\d$/)) {
×
450
        code = error.code;
×
451
    }
452

453
    res.status(code).json({
×
454
        name: error.name,
455
        message: error.message
456
    });
457
}
458

459
/**
460
 * Just fills in the transport protocol in case there is none and polling if endpoint.
461
 *
462
 * @param {Object} device           Device object containing all the information about the device.
463
 */
464
function setPollingAndDefaultTransport(device, callback) {
465
    if (!device.transport) {
115✔
466
        device.transport = 'HTTP';
45✔
467
    }
468

469
    if (device.transport === 'HTTP') {
115✔
470
        if (device.endpoint) {
65✔
471
            device.polling = false;
37✔
472
        } else {
473
            device.polling = true;
28✔
474
        }
475
    }
476

477
    callback(null, device);
115✔
478
}
479

480
/**
481
 * Device provisioning handler.
482
 *
483
 * @param {Object} device           Device object containing all the information about the provisioned device.
484
 */
485
function deviceProvisioningHandler(device, callback) {
486
    config.getLogger().debug(context, 'httpbinding.deviceProvisioningHandler %j', device);
113✔
487
    setPollingAndDefaultTransport(device, callback);
113✔
488
}
489

490
/**
491
 * Device updating handler. This handler just fills in the transport protocol in case there is none.
492
 *
493
 * @param {Object} device           Device object containing all the information about the updated device.
494
 */
495
function deviceUpdatingHandler(device, callback) {
496
    config.getLogger().debug(context, 'httpbinding.deviceUpdatingHandler %j', device);
2✔
497
    setPollingAndDefaultTransport(device, callback);
2✔
498
}
499

500
/**
501
 * This middleware checks whether there is any polling command pending to be sent to the device. If there is some,
502
 * add the command information to the return payload. Otherwise it returns an empty payload.
503
 */
504
function returnCommands(req, res, next) {
505
    function updateCommandStatus(device, commandList) {
506
        context = fillService(context, device);
6✔
507
        function createCommandUpdate(command) {
508
            return apply(
6✔
509
                iotAgentLib.setCommandResult,
510
                device.name,
511
                device.resource,
512
                req.query.k,
513
                command.name,
514
                ' ',
515
                'DELIVERED',
516
                device
517
            );
518
        }
519

520
        function cleanCommand(command) {
521
            return apply(iotAgentLib.removeCommand, device.service, device.subservice, device.id, command.name);
6✔
522
        }
523

524
        const updates = commandList.map(createCommandUpdate);
6✔
525
        const cleanCommands = commandList.map(cleanCommand);
6✔
526
        if (updates) {
6!
527
            async.parallel(updates.concat(cleanCommands), function (error, results) {
6✔
528
                if (error) {
6!
529
                    config
×
530
                        .getLogger()
531
                        .error(
532
                            context,
533
                            'Error updating command status after delivering commands for device %s',
534
                            device.id
535
                        );
536
                } else {
537
                    config
6✔
538
                        .getLogger()
539
                        .debug(
540
                            context,
541
                            'Command status updated successfully after delivering command list to device %s',
542
                            device.id
543
                        );
544
                }
545
            });
546
        }
547
    }
548

549
    function parseCommand(item) {
550
        const result = {};
6✔
551
        const cleanedValue = String(item.value).trim();
6✔
552

553
        if (cleanedValue !== '') {
6!
554
            result[item.name] = item.value;
6✔
555
        }
556
        return result;
6✔
557
    }
558

559
    function concatCommand(previous, current) {
560
        if (previous === {}) {
6!
561
            return current;
×
562
        }
563
        return _.extend(previous, current);
6✔
564
    }
565
    if (req.query && req.query.getCmd === '1') {
43✔
566
        iotAgentLib.commandQueue(req.device.service, req.device.subservice, req.deviceId, function (error, list) {
6✔
567
            if (error || !list || list.count === 0) {
6!
568
                if (req.accepts('json')) {
×
569
                    res.status(200).send({});
×
570
                } else {
571
                    res.status(200).send('');
×
572
                }
573
            } else {
574
                if (req.accepts('json')) {
6!
575
                    res.status(200).send(list.commands.map(parseCommand).reduce(concatCommand, {}));
6✔
576
                } else {
577
                    res.status(200).send(JSON.stringify(list.commands.map(parseCommand).reduce(concatCommand, {})));
×
578
                }
579
                process.nextTick(updateCommandStatus.bind(null, req.device, list.commands));
6✔
580
            }
581
        });
582
    } else if (req.accepts('json')) {
37!
583
        res.status(200).send({});
37✔
584
    } else {
585
        res.status(200).send('');
×
586
    }
587
}
588

589
function start(callback) {
590
    const baseRoot = '/';
119✔
591

592
    httpBindingServer = {
119✔
593
        server: null,
594
        app: express(),
595
        router: express.Router()
596
    };
597

598
    if (!config.getConfig().http) {
119!
599
        config
×
600
            .getLogger()
601
            .fatal(context, 'GLOBAL-002: Configuration error. Configuration object [config.http] is missing');
602
        callback(new errors.ConfigurationError('config.http'));
×
603
        return;
×
604
    }
605

606
    httpBindingServer.app.set('port', config.getConfig().http.port);
119✔
607
    httpBindingServer.app.set('host', config.getConfig().http.host || '0.0.0.0');
119!
608

609
    httpBindingServer.router.get(
119✔
610
        config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH,
119!
611
        checkMandatoryParams(true),
612
        parseData,
613
        addTimestamp,
614
        handleIncomingMeasure,
615
        returnCommands
616
    );
617

618
    httpBindingServer.router.post(
119✔
619
        config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH,
119!
620
        bodyParser.json({ strict: false }), // accept anything JSON.parse accepts
621
        checkMandatoryParams(false),
622
        parseDataMultipleMeasure,
623
        addTimestamp,
624
        handleIncomingMeasure,
625
        returnCommands
626
    );
627

628
    httpBindingServer.router.post(
119✔
629
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) +
119!
630
            '/' +
631
            constants.MEASURES_SUFIX +
632
            '/:attrValue',
633
        parserBody(),
634
        checkMandatoryParams(false),
635
        parseData, // non multiple measures are expected in this route
636
        addTimestamp,
637
        handleIncomingMeasure,
638
        returnCommands
639
    );
640

641
    httpBindingServer.router.post(
119✔
642
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_COMMANDS_PATH,
119!
643
        bodyParser.json({ strict: false }), // accept anything JSON.parse accepts.
644
        checkMandatoryParams(false),
645
        parseData,
646
        addTimestamp,
647
        isCommand,
648
        handleIncomingMeasure,
649
        returnCommands
650
    );
651

652
    httpBindingServer.router.post(
119✔
653
        (config.getConfig().iota.defaultResource || constants.HTTP_MEASURE_PATH) + constants.HTTP_CONFIGURATION_PATH,
119!
654
        bodyParser.json({ strict: false }), // accept anything JSON.parse accepts.
655
        checkMandatoryParams(false),
656
        parseData,
657
        handleConfigurationRequest
658
    );
659

660
    httpBindingServer.app.use(baseRoot, httpBindingServer.router);
119✔
661
    httpBindingServer.app.use(handleError);
119✔
662

663
    if (config.getConfig().http && config.getConfig().http.key && config.getConfig().http.cert) {
119!
664
        const privateKey = fs.readFileSync(config.getConfig().http.key, 'utf8');
×
665
        const certificate = fs.readFileSync(config.getConfig().http.cert, 'utf8');
×
666
        const credentials = { key: privateKey, cert: certificate };
×
667

668
        config.getLogger().info(context, 'HTTPS Binding listening on port %s', config.getConfig().http.port);
×
669
        httpBindingServer.server = https.createServer(credentials, httpBindingServer.app);
×
670
    } else {
671
        config.getLogger().info(context, 'HTTP Binding listening on port %s', config.getConfig().http.port);
119✔
672
        httpBindingServer.server = http.createServer(httpBindingServer.app);
119✔
673
    }
674

675
    httpBindingServer.server.listen(httpBindingServer.app.get('port'), httpBindingServer.app.get('host'), callback);
119✔
676
}
677

678
function stop(callback) {
679
    config.getLogger().info(context, 'Stopping JSON HTTP Binding: ');
119✔
680

681
    if (httpBindingServer) {
119!
682
        httpBindingServer.server.close(function () {
119✔
683
            config.getLogger().info(context, 'HTTP Binding Stopped');
119✔
684
            callback();
119✔
685
        });
686
    } else {
687
        callback();
×
688
    }
689
}
690

691
function sendPushNotifications(device, values, callback) {
692
    const executions = _.flatten(values.map(commandHandler.generateCommandExecution.bind(null, null, device)));
×
693

694
    async.series(executions, function (error) {
×
695
        callback(error);
×
696
    });
697
}
698

699
function storePollNotifications(device, values, callback) {
700
    function addPollNotification(item, innerCallback) {
701
        iotAgentLib.addCommand(device.service, device.subservice, device.id, item, innerCallback);
×
702
    }
703

704
    async.map(values, addPollNotification, callback);
×
705
}
706

707
function notificationHandler(device, values, callback) {
708
    if (device.endpoint) {
×
709
        sendPushNotifications(device, values, callback);
×
710
    } else {
711
        storePollNotifications(device, values, callback);
×
712
    }
713
}
714

715
exports.start = start;
1✔
716
exports.stop = stop;
1✔
717
exports.sendConfigurationToDevice = sendConfigurationToDevice;
1✔
718
exports.deviceProvisioningHandler = deviceProvisioningHandler;
1✔
719
exports.deviceUpdatingHandler = deviceUpdatingHandler;
1✔
720
exports.notificationHandler = notificationHandler;
1✔
721
exports.executeCommand = executeCommand;
1✔
722
exports.protocol = 'HTTP';
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc