Comparing version 0.6.1 to 0.7.0
@@ -22,2 +22,5 @@ /* | ||
, RetrySend = require('./retry_send') | ||
, parseBlock2 = require('./helpers').parseBlock2 | ||
, createBlock2 = require('./helpers').createBlock2 | ||
, getOption = require('./helpers').getOption | ||
, maxToken = Math.pow(2, 32) | ||
@@ -163,6 +166,54 @@ , maxMessageId = Math.pow(2, 16) | ||
if (req.response) | ||
var block2Buff = getOption(packet.options, 'Block2') | ||
var block2 | ||
// if we got blockwise (2) response | ||
if (block2Buff) { | ||
block2 = parseBlock2(block2Buff) | ||
// check for error | ||
if (!block2) { | ||
req.sender.reset() | ||
return req.emit('error', err) | ||
} | ||
} | ||
if (block2) { | ||
// accumulate payload | ||
req._totalPayload = Buffer.concat([req._totalPayload, packet.payload]) | ||
if (block2.moreBlock2) { | ||
// increase message id for next request | ||
delete this._msgIdToReq[req._packet.messageId] | ||
req._packet.messageId = that._nextMessageId() | ||
this._msgIdToReq[req._packet.messageId] = req | ||
// next block2 request | ||
var block2Val = createBlock2({ | ||
moreBlock2: false, | ||
num: block2.num+1, | ||
size: block2.size | ||
}) | ||
if (!block2Val) { | ||
req.sender.reset() | ||
return req.emit('error', err) | ||
} | ||
req.setOption('Block2', block2Val) | ||
req.sender.send(generate(req._packet)) | ||
return | ||
} | ||
else { | ||
// get full payload | ||
packet.payload = req._totalPayload | ||
// clear the payload incase of block2 | ||
req._totalPayload = new Buffer(0) | ||
} | ||
} | ||
if (req.response) { | ||
// it is an observe request | ||
// and we are already streaming | ||
return req.response.append(packet) | ||
} | ||
else if (block2) { | ||
delete that._tkToReq[req._packet.token.readUInt32BE(0)] | ||
} | ||
else if (!req.url.observe) | ||
@@ -178,4 +229,5 @@ // it is not, so delete the token | ||
}) | ||
} else | ||
} else { | ||
response = new IncomingMessage(packet, rsinfo) | ||
} | ||
@@ -264,2 +316,4 @@ req.response = response | ||
req._totalPayload = new Buffer(0) | ||
return req | ||
@@ -266,0 +320,0 @@ } |
@@ -118,1 +118,84 @@ /* | ||
} | ||
/* | ||
get an option value from options | ||
options array of object, in form {name: , value} | ||
name name of the object wanted to retrive | ||
return value, or null | ||
*/ | ||
module.exports.getOption = function getOption(options, name) { | ||
for (var i in options) | ||
if (options[i].name == name) | ||
return options[i].value | ||
return null | ||
} | ||
/* | ||
parse block2 | ||
value block2 value buffer | ||
return object describes block2, {moreBlock2: , num: , size: } | ||
with invalid block2 value, the function return null | ||
*/ | ||
module.exports.parseBlock2 = function parseBlock2(block2Value) { | ||
var num | ||
switch (block2Value.length) { | ||
case 1: | ||
num = block2Value[0] >> 4 | ||
break | ||
case 2: | ||
num = (block2Value[0]*256 + block2Value[1]) >> 4 | ||
break | ||
case 3: | ||
num = (block2Value[0]*256*256 + block2Value[1]*256 + block2Value[2]) >>4 | ||
break | ||
default: | ||
// Block2 is more than 3 bytes | ||
return null | ||
} | ||
// limit value of size is 1024 (2**(6+4)) | ||
if (block2Value.slice(-1)[0] == 7) { | ||
// Block size is bigger than 1024 | ||
return null | ||
} | ||
return { | ||
moreBlock2: (block2Value.slice(-1)[0] & (0x01<<3))? true:false, | ||
num: num, | ||
size: Math.pow(2, (block2Value.slice(-1)[0] & 0x07)+4) | ||
} | ||
} | ||
/* | ||
create buffer for block2 option | ||
requestedBlock object contain block2 infor, e.g. {moreBlock2: true, num: 100, size: 32} | ||
return new Buffer, carry block2 value | ||
*/ | ||
module.exports.createBlock2 = function createBlock2(requestedBlock) { | ||
var byte | ||
var szx = Math.log(requestedBlock.size)/Math.log(2) - 4 | ||
var m = ((requestedBlock.moreBlock2==true)?0:1) | ||
var num = requestedBlock.num | ||
var extraNum | ||
byte = 0 | ||
byte |= szx | ||
byte |= m << 3 | ||
byte |= (num&0xf) <<4 | ||
// total num occupy up to 5 octets | ||
// num share the higher octet of first byte, and (may) take more 2 bytes for the rest 4 octets | ||
if (num <= 0xf) { | ||
extraNum = null | ||
} | ||
else if (num <=0xfff) { | ||
extraNum = new Buffer([num/16]) | ||
} | ||
else if (num <=0xfffff) { | ||
extraNum = new Buffer(2) | ||
extraNum.writeUInt16BE(num>>4,0) | ||
} | ||
else { | ||
// too big block2 number | ||
return null | ||
} | ||
return (extraNum)? Buffer.concat([extraNum, new Buffer([byte])]):new Buffer([byte]) | ||
} |
@@ -50,2 +50,5 @@ /* | ||
// default max packet size | ||
p.maxPacketSize = 1280 | ||
module.exports = p |
@@ -20,2 +20,5 @@ /* | ||
, RetrySend = require('./retry_send') | ||
, parseBlock2 = require('./helpers').parseBlock2 | ||
, createBlock2 = require('./helpers').createBlock2 | ||
, getOption = require('./helpers').getOption | ||
@@ -66,2 +69,4 @@ function CoAPServer(options, listener) { | ||
if (listener) | ||
@@ -117,3 +122,3 @@ this.on('request', listener) | ||
, cached = lru.peek(toKey(rsinfo.address, rsinfo.port, packet, true)) | ||
, Message = OutgoingMessage | ||
, Message = OutMessage | ||
, that = this | ||
@@ -153,3 +158,3 @@ , request | ||
if (Message === OutgoingMessage) { | ||
if (Message === OutMessage) { | ||
sender.on('error', response.emit.bind(response, 'error')) | ||
@@ -174,2 +179,13 @@ } else { | ||
// if (response instanceof OutMessage) { | ||
response._request = request._packet | ||
// } | ||
// todo: | ||
// should use mem cache to buffer responses | ||
// dont alway bother uper layer, especially when the return is in blockwise (2) | ||
// if (cachedPayload) | ||
// response.end(cachedPayload); | ||
// else | ||
// this.emit('request', request, response) | ||
this.emit('request', request, response) | ||
@@ -187,2 +203,96 @@ } | ||
/* | ||
new out message | ||
inherit from OutgoingMessage | ||
to handle cached answer and blockwise (2) | ||
*/ | ||
function OutMessage() { | ||
OutgoingMessage.apply(this, Array.prototype.slice.call(arguments)); | ||
} | ||
util.inherits(OutMessage, OutgoingMessage) | ||
// maxBlock2 is in formular 2**(i+4), and must <= 2**(6+4) | ||
var maxBlock2 = Math.pow(2, Math.floor(Math.log(parameters.maxPacketSize)/Math.log(2))) | ||
if (maxBlock2 > Math.pow(2, (6+4))) | ||
maxBlock2 = Math.pow(2, (6+4)) | ||
OutMessage.prototype.end= function(payload) { | ||
var that = this | ||
var block2Buff = getOption(this._request.options, 'Block2') | ||
var requestedBlockOption | ||
// if we got blockwise (2) resquest | ||
if (block2Buff) { | ||
requestedBlockOption = parseBlock2(block2Buff) | ||
// bad option | ||
if (!requestedBlockOption) { | ||
that.statusCode = '4.02' | ||
return OutgoingMessage.prototype.end.call(that) | ||
} | ||
} | ||
// if payload is suitable for ONE message, shoot it out | ||
if (!payload || | ||
((!requestedBlockOption) && (payload.length < parameters.maxPacketSize))) | ||
return OutgoingMessage.prototype.end.call(this, payload) | ||
// for the first request, block2 option may be missed | ||
if (!requestedBlockOption) | ||
requestedBlockOption = { | ||
size: maxBlock2, | ||
num: 0 | ||
} | ||
// block2 size should not bigger than maxBlock2 | ||
if (requestedBlockOption.size > maxBlock2) | ||
requestedBlockOption.size = maxBlock2 | ||
// block number should have limit | ||
// 0 base counter for totalBlock, hence use floor (vs ceil) | ||
var totalBlock = Math.floor(payload.length/requestedBlockOption.size) | ||
var isLastBlock | ||
if (requestedBlockOption.num < totalBlock) | ||
isLastBlock = false | ||
else if (requestedBlockOption.num == totalBlock) | ||
isLastBlock = true | ||
else { | ||
// precondition fail, may request for out of range block | ||
that.statusCode = '4.02' | ||
return OutgoingMessage.prototype.end.call(that) | ||
} | ||
var block2 = createBlock2({ | ||
moreBlock2: isLastBlock, | ||
num: requestedBlockOption.num, | ||
size: requestedBlockOption.size | ||
}) | ||
if (!block2) { | ||
// this catch never be match, | ||
// since we're gentleman, just handle it | ||
that.statusCode = '4.02' | ||
return OutgoingMessage.prototype.end.call(that) | ||
} | ||
this.setOption('Block2', block2) | ||
this.setOption('ETag', _toETag(payload)) | ||
OutgoingMessage.prototype.end.call(this, payload.slice((requestedBlockOption.num)*requestedBlockOption.size, (requestedBlockOption.num+1)*requestedBlockOption.size)) | ||
}; | ||
/* | ||
calculate id of a payload by xor each 2-byte-block from it | ||
use to generate etag | ||
payload an input buffer, represent payload need to generate id (hash) | ||
id return var, is a buffer(2) | ||
*/ | ||
function _toETag(payload) { | ||
var id = new Buffer([0,0]) | ||
var i = 0 | ||
do { | ||
id[0] ^= payload[i] | ||
id[1] ^= payload[i+1] | ||
i += 2 | ||
} while (i<payload.length) | ||
return id | ||
} | ||
module.exports = CoAPServer |
{ | ||
"name": "coap", | ||
"version": "0.6.1", | ||
"version": "0.7.0", | ||
"description": "A CoAP library for node modelled after 'http'", | ||
@@ -37,6 +37,6 @@ "main": "index.js", | ||
"dependencies": { | ||
"bl": "~0.7.0", | ||
"coap-packet": "~0.1.8", | ||
"bl": "~0.7.0", | ||
"lru-cache": "~2.5.0" | ||
} | ||
} |
@@ -28,7 +28,8 @@ node-coap | ||
This library follows the | ||
[draft-18](http://tools.ietf.org/html/draft-ietf-core-coap-18) of the standard. | ||
Moreover, it supports the | ||
[observe-11](http://tools.ietf.org/html/draft-ietf-core-observe-11) | ||
specification. | ||
This library follows: | ||
* [draft-18](http://tools.ietf.org/html/draft-ietf-core-coap-18) of CoAP. | ||
* [observe-11](http://tools.ietf.org/html/draft-ietf-core-observe-11) | ||
for the observe specification. | ||
* [block-14](http://tools.ietf.org/id/draft-ietf-core-block-14.txt) for | ||
the blockwise specification. | ||
@@ -262,4 +263,8 @@ It does not parse the protocol but it use | ||
Also, `'Content-Type'` is aliased to `'Content-Format'` for HTTP | ||
compatibility. | ||
compatibility.gg | ||
Since v0.7.0, this library supports blockwise transfers, you can trigger | ||
them by adding a `req.setOption('Block2', new Buffer([0x2]))` to the | ||
output of [request](#request). | ||
See the | ||
@@ -449,2 +454,3 @@ [spec](http://tools.ietf.org/html/draft-ietf-core-coap-18#section-5.4) | ||
<tr><th align="left">Matteo Collina</th><td><a href="https://github.com/mcollina">GitHub/mcollina</a></td><td><a href="https://twitter.com/matteocollina">Twitter/@matteocollina</a></td></tr> | ||
<tr><th align="left">Nguyen Quoc Dinh</th><td><a href="https://github.com/nqd">GitHub/nqd</a></td><td><a href="https://twitter.com/nqdinh">Twitter/@nqdinh</a></td></tr> | ||
</tbody></table> | ||
@@ -451,0 +457,0 @@ |
@@ -184,13 +184,2 @@ | ||
it('should have res emitting an error if the message is too big', function(done) { | ||
send(generate()) | ||
server.on('request', function(req, res) { | ||
res.on('error', function() { | ||
done() | ||
}) | ||
res.end(new Buffer(1280)) | ||
}) | ||
}) | ||
it('should expose the options', function(done) { | ||
@@ -197,0 +186,0 @@ var options = [{ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
119337
31
3260
461