Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

coap

Package Overview
Dependencies
Maintainers
1
Versions
68
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

coap - npm Package Compare versions

Comparing version 0.0.1 to 0.1.0

.travis.yml

100

index.js
const bl = require('bl')
, dgram = require('dgram')
, parse = require('coap-packet').parse
, generate = require('coap-packet').generate
, URL = require('url')
, coapPort = 5683
const bl = require('bl')
, dgram = require('dgram')
, backoff = require('backoff')
, parse = require('coap-packet').parse
, generate = require('coap-packet').generate
, URL = require('url')
, Server = require('./lib/server')
, IncomingMessage = require('./lib/incoming_message')
, OutgoingMessage = require('./lib/outgoing_message')
, parameters = require('./lib/parameters')
, optionsConv = require('./lib/option_converter')
module.exports.request = function(url) {
var req = bl()
, client = dgram.createSocket('udp4')
, packet = { options: [] }
var req
, bOff = backoff.exponential({
randomisationFactor: 0.2,
initialDelay: 1222,
maxDelay: parameters.maxTransmitSpan * 1000
})
, timer
, cleanUp = function() {
client.close()
bOff.reset()
clearTimeout(timer)
}
, client = dgram.createSocket('udp4', function(msg, rsinfo) {
req.emit('response', new IncomingMessage(parse(msg), rsinfo))
})
, message
if (typeof url === 'string') {
url = URL.parse(url)
, send
send = function(buf) {
if (Buffer.isBuffer(buf))
message = buf
client.send(message, 0, message.length,
url.port, url.hostname || url.host)
bOff.backoff()
}
packet.code = url.method || 'GET'
url.port = url.port || coapPort
req = new OutgoingMessage({}, send)
urlPropertyToPacketOption(url, packet, 'pathname', 'Uri-Path', '/')
urlPropertyToPacketOption(url, packet, 'query', 'Uri-Query', '&')
if (typeof url === 'string')
url = URL.parse(url)
req.statusCode = url.method || 'GET'
url.port = url.port || parameters.coapPort
urlPropertyToPacketOption(url, req, 'pathname', 'Uri-Path', '/')
urlPropertyToPacketOption(url, req, 'query', 'Uri-Query', '&')
client.on('error', req.emit.bind(req, 'error'))
req.on('finish', function() {
packet.payload = req.slice()
message = generate(packet)
req.on('error', cleanUp)
client.send(message, 0, message.length, url.port, url.hostname || url.host, function(err, bytes) {
client.close()
})
})
bOff.failAfter(parameters.maxRetransmit - 1)
bOff.on('ready', send)
timer = setTimeout(function() {
var err = new Error('No reply in ' + parameters.exchangeLifetime + 's')
req.emit('error', err)
}, parameters.exchangeLifetime * 1000)
return req
}
function urlPropertyToPacketOption(url, packet, property, option, separator) {
module.exports.createServer = Server
function urlPropertyToPacketOption(url, req, property, option, separator) {
if (url[property])
url[property].split(separator)
.filter(function(part) { return part !== '' })
.forEach(function(part) {
req.setOption(option, url[property].split(separator)
.filter(function(part) { return part !== '' })
.map(function(part) {
var buf = new Buffer(Buffer.byteLength(part))
buf.write(part)
packet.options.push({
name: option
, value: buf
})
})
return buf
}))
}
module.exports.registerOption = optionsConv.registerOption
module.exports.registerFormat = optionsConv.registerFormat
{
"name": "coap",
"version": "0.0.1",
"version": "0.1.0",
"description": "A CoAP library for node modelled after 'http'",

@@ -14,5 +14,3 @@ "main": "index.js",

"pre-commit": [
"test",
"lint-lib",
"lint-tests"
"test"
],

@@ -32,8 +30,12 @@ "keywords": [

"chai": "~1.8.0",
"mocha": "~1.13.0"
"mocha": "~1.13.0",
"timekeeper": "0.0.3",
"sinon": "~1.7.3"
},
"dependencies": {
"coap-packet": "~0.1.1",
"bl": "~0.4.2"
"coap-packet": "~0.1.3",
"bl": "~0.4.2",
"backoff": "~2.3.0",
"lru-cache": "~2.3.1"
}
}

@@ -7,5 +7,14 @@ node-coap

__node-coap__ is an _highly experimental_ client and (in the future)
server library for CoAP modelled after the `http` module.
__node-coap__ is an _highly experimental_ client and server library for CoAP modelled after the `http` module.
* <a href="#intro">Introduction</a>
* <a href="#install">Installation</a>
* <a href="#basic">Basic Example</a>
* <a href="#api">API</a>
* <a href="#contributing">Contributing</a>
* <a href="#licence">Licence &amp; copyright</a>
<a name="intro"></a>
## Introduction
What is CoAP?

@@ -22,10 +31,14 @@ > Constrained Application Protocol (CoAP) is a software protocol

**node-coap** is an **OPEN Open Source Project**, see the <a href="#contributing">Contributing</a> section to find out what this means.
This has been tested only on node v0.10.
<a name="install"></a>
## Installation
```
$: npm install coap --save
$ npm install coap --save
```
<a name="basic"></a>
## Basic Example

@@ -36,25 +49,34 @@

```
const dgram = require('dgram')
, coapPacket = require('coap-packet')
, parse = packet.parse
, payload = new Buffer('Hello World')
, port = 41234
, server = dgram.createSocket("udp4")
, coap = require('coap')
```js
const coap = require('coap')
, server = coap.createServer()
server.bind(port, function() {
coap.request('coap://localhost:' + port).end(paylaod)
server.on('request', function(req, res) {
res.end('Hello ' + req.url.split('/')[1] + '\n')
})
server.on('message', function(data) {
console.log(parse(data).payload.toString())
server.close()
// the default CoAP port is 5683
server.listen(function() {
var req = coap.request('coap://localhost/Matteo')
req.on('response', function(res) {
res.pipe(process.stdout)
res.on('end', function() {
process.exit(0)
})
})
req.end()
})
```
<a name="api"></a>
## API
* <a href="#parse"><code>coap.<b>request()</b></code></a>
* <a href="#request"><code>coap.<b>request()</b></code></a>
* <a href="#createServer"><code>coap.<b>createServer()</b></code></a>
* <a href="#incoming"><code>IncomingMessage</b></code></a>
* <a href="#outgoing"><code>OutgoingMessage</b></code></a>
<a name="request"></a>
### request(url)

@@ -78,9 +100,179 @@

`coap.request()` returns an instance of `stream.Writable`. If you need
`coap.request()` returns an instance of <a
href='#incoming'><code>IncomingMessage</code></a>.
If you need
to add a payload, just `pipe` into it.
Otherwise, you __must__ call `end` to submit the request.
#### Event: 'response'
`function (response) { }`
Emitted when a response is received.
`response` is
an instance of <a
href='#incoming'><code>IncomingMessage</code></a>.
<a name="createServer"></a>
### createServer([requestListener])
Returns a new CoAP Server object.
The `requestListener` is a function which is automatically
added to the `'request'` event.
#### Event: 'request'
`function (request, response) { }`
Emitted each time there is a request.
`request` is an instance of <a
href='#incoming'><code>IncomingMessage</code></a> and `response` is
an instance of <a
href='#outgoing'><code>OutgoingMessage</code></a>.
#### server.listen(port, [hostname], [callback])
Begin accepting connections on the specified port and hostname. If the
hostname is omitted, the server will accept connections directed to any
IPv4 address (`INADDR_ANY`).
To listen to a unix socket, supply a filename instead of port and hostname.
This function is asynchronous.
#### server.close([callback])
Closes the server.
This function is synchronous, but it provides an asynchronous callback
for convenience.
<a name="outgoing"></a>
### OutgoingMessage
An `OutgoingMessage` object is returned by `coap.request` or
emitted by the `coap.createServer` `'response'` event.
It may be used to access response status, headers and data.
It implements the [Writable
Stream](http://nodejs.org/api/stream.html#stream_class_stream_writable) interface, as well as the
following additional methods and properties.
#### message.statusCode
The CoAP code ot the message.
It is HTTP-compatible, as it can be passed `404`.
#### message.setOption(name, value)
Sets a single option value.
All the options are in binary format, except for
`'Content-Format'`, `'Accept'` and `'ETag'`.
See <a href='#registerOption'> to know how to register more.
Use an array of buffers
if you need to send multiple options with the same name.
If you need to pass a custom option, pass a string containing a
Example:
message.setOption("Content-Format", "application/json");
or
message.setOption("555", [new Buffer('abcde',
new Buffer('ghi')]);
`setOption` is also aliased as `setHeader` for HTTP API
compatibility.
Also, `'Content-Type'` is aliased to `'Content-Format'` for HTTP
compatibility.
See the
[spec](http://tools.ietf.org/html/draft-ietf-core-coap-18#section-5.4)
for all the possible options.
<a name="incoming"></a>
### IncomingMessage
An `IncomingMessage` object is created by `coap.createServer` or
`coap.request`
and passed as the first argument to the `'request'` and `'response'` event
respectively. It may be used to access response status, headers and data.
It implements the [Readable
Stream](http://nodejs.org/api/stream.html#stream_class_stream_readable) interface, as well as the
following additional methods and properties.
#### message.payload
The full payload of the message, as a Buffer.
#### message.options
All the CoAP options, as parsed by
[CoAP-packet](http://github.com/mcollina/coap-packet).
All the options are in binary format, except for
`'Content-Format'`, `'Accept'` and `'ETag'`.
See <a href='#registerOption'> to know how to register more.
See the
[spec](http://tools.ietf.org/html/draft-ietf-core-coap-18#section-5.4)
for all the possible options.
#### message.headers
All the CoAP options that can be represented in a human-readable format.
Currently they are only `'Content-Format'`, `'Accept'` and
`'ETag'`.
See <a href='#registerOption'> to know how to register more.
Also, `'Content-Type'` is aliased to `'Content-Format'` for HTTP
compatibility.
#### message.code
The CoAP code of the message.
#### message.method
The method of the message, it might be
`'GET'`, `'POST'`, `'PUT'`, `'DELETE'` or `null`.
It is null if the CoAP code cannot be parsed into a method, i.e. it is
not in the '0.' range.
#### message.url
The URL of the request, e.g.
`'coap://localhost:12345/hello/world?a=b&b=c'`.
<a name="contributing"></a>
## Contributing
__node-coap__ is an **OPEN Open Source Project**. This means that:
> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
See the [CONTRIBUTING.md](https://github.com/mcollina/node-coap/blob/master/CONTRIBUTING.md) file for more details.
## Limitations
At the moment only non-confirmable messages are supported (NON in the
CoAP spec). This means less reliability, as there is no way for a client
to know if the server has received the message.
Moreover, the maximum packet size is 1280, as the
[blockwise](http://datatracker.ietf.org/doc/draft-ietf-core-block/) is
not supported yet.
The [observe](http://datatracker.ietf.org/doc/draft-ietf-core-observe/)
support is planned too.
## Contributors
Coap-Packet is only possible due to the excellent work of the following contributors:
__node-coap__ is only possible due to the excellent work of the following contributors:

@@ -87,0 +279,0 @@ <table><tbody>

var coap = require('../')
, parse = require('coap-packet').parse
, generate = require('coap-packet').generate
, dgram = require('dgram')
, bl = require('bl')
, request = coap.request
const coap = require('../')
, parse = require('coap-packet').parse
, generate = require('coap-packet').generate
, dgram = require('dgram')
, bl = require('bl')
, sinon = require('sinon')
, request = coap.request

@@ -59,2 +60,12 @@ describe('request', function() {

it('should error if the message is too big', function(done) {
var req = request('coap://localhost:' + port)
req.on('error', function() {
done()
})
req.end(new Buffer(1280))
})
it('should imply a default port', function(done) {

@@ -143,2 +154,305 @@ server2 = dgram.createSocket('udp4')

})
it('should emit a response', function(done) {
var req = request({
port: port
})
server.on('message', function(msg, rsinfo) {
var packet = parse(msg)
, toSend = generate({
messageId: packet.messageId
, token: packet.token
, payload: new Buffer('42')
})
server.send(toSend, 0, toSend.length, rsinfo.port, rsinfo.address)
})
req.on('response', function(res) {
res.pipe(bl(function(err, data) {
expect(data).to.eql(new Buffer('42'))
done()
}))
})
req.end()
})
it('should allow to add an option', function(done) {
var req = request({
port: port
})
, buf = new Buffer(3)
req.setOption('ETag', buf)
req.end()
server.on('message', function(msg) {
expect(parse(msg).options[0].name).to.eql('ETag')
expect(parse(msg).options[0].value).to.eql(buf)
done()
})
})
it('should overwrite the option', function(done) {
var req = request({
port: port
})
, buf = new Buffer(3)
req.setOption('ETag', new Buffer(3))
req.setOption('ETag', buf)
req.end()
server.on('message', function(msg) {
expect(parse(msg).options[0].value).to.eql(buf)
done()
})
})
it('should alias setOption to setHeader', function(done) {
var req = request({
port: port
})
, buf = new Buffer(3)
req.setHeader('ETag', buf)
req.end()
server.on('message', function(msg) {
expect(parse(msg).options[0].value).to.eql(buf)
done()
})
})
it('should set multiple options', function(done) {
var req = request({
port: port
})
, buf1 = new Buffer(3)
, buf2 = new Buffer(3)
req.setOption('433', [buf1, buf2])
req.end()
server.on('message', function(msg) {
expect(parse(msg).options[0].value).to.eql(buf1)
expect(parse(msg).options[1].value).to.eql(buf2)
done()
})
})
it('should alias the \'Content-Format\' option to \'Content-Type\'', function(done) {
var req = request({
port: port
})
req.setOption('Content-Type', new Buffer([0]))
req.end()
server.on('message', function(msg) {
expect(parse(msg).options[0].name).to.eql('Content-Format')
expect(parse(msg).options[0].value).to.eql(new Buffer([0]))
done()
})
})
var formatsString = {
'text/plain': new Buffer([0])
, 'application/link-format': new Buffer([40])
, 'application/xml': new Buffer([41])
, 'application/octet-stream': new Buffer([42])
, 'application/exi': new Buffer([47])
, 'application/json': new Buffer([50])
}
describe('with the \'Content-Format\' header in the outgoing message', function() {
function buildTest(format, value) {
it('should parse ' + format, function(done) {
var req = request({
port: port
})
req.setOption('Content-Format', format)
req.end()
server.on('message', function(msg) {
expect(parse(msg).options[0].value).to.eql(value)
done()
})
})
}
for (var format in formatsString) {
buildTest(format, formatsString[format])
}
})
describe('with the \'Accept\' header in the outgoing message', function() {
function buildTest(format, value) {
it('should parse ' + format, function(done) {
var req = request({
port: port
})
req.setHeader('Accept', format)
req.end()
server.on('message', function(msg) {
expect(parse(msg).options[0].value).to.eql(value)
done()
})
})
}
for (var format in formatsString) {
buildTest(format, formatsString[format])
}
})
describe('with the \'Content-Format\' in the response', function() {
function buildResponse(value) {
return function(msg, rsinfo) {
var packet = parse(msg)
, toSend = generate({
messageId: packet.messageId
, token: packet.token
, options: [{
name: 'Content-Format'
, value: value
}]
})
server.send(toSend, 0, toSend.length, rsinfo.port, rsinfo.address)
}
}
function buildTest(format, value) {
it('should parse ' + format, function(done) {
var req = request({
port: port
})
server.on('message', buildResponse(value))
req.on('response', function(res) {
expect(res.options[0].value).to.eql(format)
done()
})
req.end()
})
it('should include ' + format + ' in the headers', function(done) {
var req = request({
port: port
})
server.on('message', buildResponse(value))
req.on('response', function(res) {
expect(res.headers['Content-Format']).to.eql(format)
expect(res.headers['Content-Type']).to.eql(format)
done()
})
req.end()
})
}
for (var format in formatsString) {
buildTest(format, formatsString[format])
}
})
it('should include \'ETag\' in the response headers', function(done) {
var req = request({
port: port
})
server.on('message', function(msg, rsinfo) {
var packet = parse(msg)
, toSend = generate({
messageId: packet.messageId
, token: packet.token
, options: [{
name: 'ETag'
, value: new Buffer('abcdefgh')
}]
})
server.send(toSend, 0, toSend.length, rsinfo.port, rsinfo.address)
})
req.on('response', function(res) {
expect(res.headers).to.have.property('ETag', 'abcdefgh')
done()
})
req.end()
})
describe('retries', function() {
var clock
beforeEach(function() {
clock = sinon.useFakeTimers()
})
afterEach(function() {
clock.restore()
})
function fastForward(increase, max) {
clock.tick(increase)
if (increase < max)
setImmediate(fastForward.bind(null, increase, max - increase))
}
it('should error after ~247 seconds', function(done) {
var req = request('coap://localhost:' + port)
req.end()
req.on('error', function(err) {
expect(err).to.have.property('message', 'No reply in 247s')
done()
})
clock.tick(247 * 1000)
})
it('should retry four times before erroring', function(done) {
var req = request('coap://localhost:' + port)
, messages = 0
req.end()
server.on('message', function(msg) {
messages++
})
req.on('error', function(err) {
// original one plus 4 retries
expect(messages).to.eql(5)
done()
})
fastForward(100, 247 * 1000)
})
it('should retry four times before 45s', function(done) {
var req = request('coap://localhost:' + port)
, messages = 0
req.end()
server.on('message', function(msg) {
messages++
})
setTimeout(function() {
// original one plus 4 retries
expect(messages).to.eql(5)
done()
}, 45 * 1000)
fastForward(100, 45 * 1000)
})
})
})

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc