Comparing version 0.3.1 to 0.4.0
118
index.js
const bl = require('bl') | ||
, dgram = require('dgram') | ||
, parse = require('coap-packet').parse | ||
, generate = require('coap-packet').generate | ||
, URL = require('url') | ||
const optionsConv = require('./lib/option_converter') | ||
, Server = require('./lib/server') | ||
, IncomingMessage = require('./lib/incoming_message') | ||
, OutgoingMessage = require('./lib/outgoing_message') | ||
, ObserveStream = require('./lib/observe_read_stream') | ||
, parameters = require('./lib/parameters') | ||
, optionsConv = require('./lib/option_converter') | ||
, RetrySend = require('./lib/retry_send') | ||
, Agent = require('./lib/agent') | ||
, globalAgent = new Agent() | ||
module.exports.request = function(url) { | ||
var req, sender, response | ||
var agent, req | ||
, closing = false | ||
, acking = false | ||
, cleanUp = function() { | ||
closing = true | ||
sender.reset() | ||
if (!acking) | ||
client.close() | ||
} | ||
, client = dgram.createSocket('udp4', function(msg, rsinfo) { | ||
var packet = parse(msg) | ||
, buf | ||
if (packet.confirmable) { | ||
buf = generate({ | ||
code: '0.00' | ||
, ack: true | ||
, messageId: packet.messageId | ||
, token: packet.token | ||
}) | ||
acking = true | ||
client.send(buf, 0, buf.length, rsinfo.port, rsinfo.address, function() { | ||
if (closing) | ||
client.close() | ||
}) | ||
} | ||
sender.reset() | ||
if (packet.code == '0.00') | ||
return | ||
if (url.observe && response) | ||
return response.append(packet) | ||
if (url.observe) { | ||
response = new ObserveStream(packet, rsinfo) | ||
response.on('close', cleanUp) | ||
} else | ||
response = new IncomingMessage(packet, rsinfo) | ||
req.emit('response', response) | ||
}) | ||
if (typeof url === 'string') | ||
url = URL.parse(url) | ||
sender = new RetrySend(client, url.port, url.hostname || url.host) | ||
req = new OutgoingMessage({}, function(req, packet) { | ||
var buf | ||
if (url.confirmable !== false) { | ||
packet.confirmable = true | ||
} | ||
try { | ||
buf = generate(packet) | ||
} catch(err) { | ||
return req.emit('error', err) | ||
} | ||
sender.send(buf) | ||
}) | ||
req.statusCode = url.method || 'GET' | ||
urlPropertyToPacketOption(url, req, 'pathname', 'Uri-Path', '/') | ||
urlPropertyToPacketOption(url, req, 'query', 'Uri-Query', '&') | ||
client.on('error', req.emit.bind(req, 'error')) | ||
sender.on('error', req.emit.bind(req, 'error')) | ||
req.on('error', cleanUp) | ||
if (url.observe) | ||
req.setOption('Observe', null) | ||
if (url.agent) | ||
agent = url.agent | ||
else if (url.agent === false) | ||
agent = new Agent() | ||
else | ||
req.on('response', cleanUp) | ||
agent = globalAgent | ||
return req | ||
return agent.request(url) | ||
} | ||
@@ -107,15 +22,6 @@ | ||
function urlPropertyToPacketOption(url, req, property, option, separator) { | ||
if (url[property]) | ||
req.setOption(option, url[property].split(separator) | ||
.filter(function(part) { return part !== '' }) | ||
.map(function(part) { | ||
module.exports.Agent = Agent | ||
module.exports.globalAgent = globalAgent | ||
var buf = new Buffer(Buffer.byteLength(part)) | ||
buf.write(part) | ||
return buf | ||
})) | ||
} | ||
module.exports.registerOption = optionsConv.registerOption | ||
module.exports.registerFormat = optionsConv.registerFormat |
@@ -35,4 +35,11 @@ | ||
RetrySend.prototype._send = function(avoidBackoff) { | ||
var that = this | ||
this._sock.send(this._message, 0, this._message.length, | ||
this._port, this._host) | ||
this._port, this._host, function(err, bytes) { | ||
that.emit('sent', err, bytes) | ||
if (err) { | ||
that.emit('error', err) | ||
} | ||
}) | ||
@@ -42,3 +49,3 @@ if (!avoidBackoff) | ||
this.emit('sent', this._message) | ||
this.emit('sending', this._message) | ||
} | ||
@@ -45,0 +52,0 @@ |
@@ -153,2 +153,3 @@ const dgram = require('dgram') | ||
request.rsinfo = rsinfo | ||
response.statusCode = '2.05' | ||
@@ -155,0 +156,0 @@ |
{ | ||
"name": "coap", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "A CoAP library for node modelled after 'http'", | ||
@@ -23,3 +23,5 @@ "main": "index.js", | ||
"udp", | ||
"observe" | ||
"observe", | ||
"internet of things", | ||
"messaging" | ||
], | ||
@@ -26,0 +28,0 @@ "author": "Matteo Collina <hello@matteocollina.com>", |
@@ -27,3 +27,3 @@ node-coap | ||
Moreover, it supports the | ||
[observe-10](http://tools.ietf.org/html/draft-ietf-core-observe-10) | ||
[observe-11](http://tools.ietf.org/html/draft-ietf-core-observe-11) | ||
specification. | ||
@@ -83,4 +83,6 @@ | ||
* <a href="#observewrite"><code>ObserveWriteStream</b></code></a> | ||
* <a href="#registerOption"><code>coap.registerOption()</b></code></a> | ||
* <a href="#registerFormat"><code>coap.registerFormat()</b></code></a> | ||
* <a href="#registerOption"><code>coap.<b>registerOption()</b></code></a> | ||
* <a href="#registerFormat"><code>coap.<b>registerFormat()</b></code></a> | ||
* <a href="#agent"><code>coap.<b>Agent</b></code></a> | ||
* <a href="#globalAgent"><code>coap.<b>globalAgent</b></code></a> | ||
@@ -110,2 +112,10 @@ ------------------------------------------------------- | ||
e.g. 'a=b&c=d' | ||
- `observe`: send a CoAP observe message, allowing the streaming of | ||
updates from the server. | ||
- `agent`: Controls [`Agent`](#agent) behavior. Possible values: | ||
* `undefined` (default): use [`globalAgent`](#globalAgent), a single socket for all | ||
concurrent requests. | ||
* [`Agent`](#agent) object: explicitly use the passed in [`Agent`](#agent). | ||
* `false`: opts out of socket reuse with an [`Agent`](#agent), each request uses a | ||
new UDP socket. | ||
@@ -131,3 +141,3 @@ `coap.request()` returns an instance of <a | ||
Which represent the updates coming from the server, according to the | ||
[observe spec](http://tools.ietf.org/html/draft-ietf-core-observe-10). | ||
[observe spec](http://tools.ietf.org/html/draft-ietf-core-observe-11). | ||
@@ -281,2 +291,7 @@ ------------------------------------------------------- | ||
#### message.rsinfo | ||
The sender informations, as emitted by the socket. | ||
See [the `dgram` docs](http://nodejs.org/api/dgram.html#dgram_event_message) for details | ||
------------------------------------------------------- | ||
@@ -352,2 +367,19 @@ <a name="observeread"></a> | ||
------------------------------------------------------- | ||
<a name="agent"></a> | ||
### coap.Agent() | ||
An Agent encapsulate an UDP Socket. It uses a combination of `messageId` | ||
and `token` to distinguish between the different exchanges. | ||
The socket will auto-close itself when no more exchange are in place. | ||
By default, no UDP socket are open, and it is opened on demand to send | ||
the messages. | ||
------------------------------------------------------- | ||
<a name="globalAgent"></a> | ||
### coap.globalAgent | ||
The default [`Agent`](#agent). | ||
<a name="contributing"></a> | ||
@@ -354,0 +386,0 @@ ## Contributing |
@@ -139,2 +139,62 @@ | ||
}) | ||
it('should support multiple observe to the same destination', function(done) { | ||
var req1 = coap.request({ | ||
port: port | ||
, method: 'GET' | ||
, observe: true | ||
, pathname: '/a' | ||
}).end() | ||
, req2 = coap.request({ | ||
port: port | ||
, method: 'GET' | ||
, observe: true | ||
, pathname: '/b' | ||
}).end() | ||
, completed = 2 | ||
server.on('request', function(req, res) { | ||
res.write('hello') | ||
setTimeout(function() { | ||
res.end('world') | ||
}, 10) | ||
}) | ||
;[req1, req2].forEach(function(req) { | ||
var local = 2 | ||
req.on('response', function(res) { | ||
res.on('data', function(data) { | ||
if (--local == 0) | ||
--completed | ||
if (completed === 0) | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
it('should reuse the same socket for two concurrent requests', function(done) { | ||
var req1 = coap.request({ | ||
port: port | ||
, method: 'GET' | ||
, pathname: '/a' | ||
}).end() | ||
, req2 = coap.request({ | ||
port: port | ||
, method: 'GET' | ||
, pathname: '/b' | ||
}).end() | ||
, first | ||
server.on('request', function(req, res) { | ||
res.end('hello') | ||
if (!first) | ||
first = req.rsinfo | ||
else { | ||
expect(req.rsinfo).to.eql(first) | ||
done() | ||
} | ||
}) | ||
}) | ||
}) |
@@ -239,4 +239,2 @@ | ||
messageId: packet.messageId | ||
, token: packet.token | ||
, payload: new Buffer('') | ||
, ack: true | ||
@@ -278,3 +276,2 @@ , code: '0.00' | ||
messageId: packet.messageId | ||
, token: packet.token | ||
, ack: true | ||
@@ -709,3 +706,2 @@ , code: '0.00' | ||
messageId: packet.messageId | ||
, token: packet.token | ||
, code: '0.00' | ||
@@ -712,0 +708,0 @@ , ack: true |
@@ -97,2 +97,13 @@ | ||
it('should include a rsinfo', function(done) { | ||
send(generate()) | ||
server.on('request', function(req, res) { | ||
expect(req).to.have.property('rsinfo') | ||
expect(req.rsinfo).to.have.property('address') | ||
expect(req.rsinfo).to.have.property('port') | ||
res.end('hello') | ||
done() | ||
}) | ||
}) | ||
;['GET', 'POST', 'PUT', 'DELETE'].forEach(function(method) { | ||
@@ -627,2 +638,13 @@ it('should include the \'' + method + '\' method', function(done) { | ||
it('should include a rsinfo', function(done) { | ||
doObserve() | ||
server.on('request', function(req, res) { | ||
expect(req).to.have.property('rsinfo') | ||
expect(req.rsinfo).to.have.property('address') | ||
expect(req.rsinfo).to.have.property('port') | ||
res.end('hello') | ||
done() | ||
}) | ||
}) | ||
it('should emit a request with \'Observe\' in the headers', function(done) { | ||
@@ -629,0 +651,0 @@ doObserve() |
90983
28
2397
408
5