import { nextPort } from './common' ; import { createServer , request } from '../index' ; import { generate , Packet , parse } from 'coap-packet' ; import { getOption , parseBlock2 } from '../lib/helpers' ; import { generateBlockOption , parseBlockOption , exponentToByteSize , byteSizeToExponent } from '../lib/block' ; import dgram from 'dgram' ; import { expect } from 'chai' ; describe ( 'blockwise2' , function ( ) { let server ; let port ; let clientPort ; let client ; let bufferVal ; const payload = Buffer . alloc ( 1536 ) ; beforeEach ( function ( done ) { bufferVal = 0 ; port = nextPort ( ) ; server = createServer ( ) ; server . listen ( port , done ) ; } ) ; beforeEach ( function ( done ) { clientPort = nextPort ( ) ; client = dgram . createSocket ( 'udp4' ) ; client . bind ( clientPort , done ) ; } ) ; afterEach ( function ( ) { server . close ( ) ; client . close ( ) ; } ) ; function send ( message ) { client . send ( message , 0 , message . length , port , '127.0.0.1' ) ; } function nextBufferVal ( ) { if ( bufferVal > 255 ) { bufferVal = 0 ; } return bufferVal ++ ; } function fillPayloadBuffer ( buffer ) { for ( let i = 0 ; i < buffer . length ; i ++ ) { buffer [ i ] = nextBufferVal ( ) ; } return buffer ; } it ( 'should server not use blockwise in response when payload fit in one packet' , function ( done ) { const payload = Buffer . alloc ( 100 ) ; request ( { port : port } ) . on ( 'response' , ( res ) => { let blockwiseResponse = false ; for ( const i in res . options ) { if ( res . options [ i ] . name === 'Block2' ) { blockwiseResponse = true ; break ; } } expect ( blockwiseResponse ) . to . eql ( false ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; it ( 'should use blockwise in response when payload bigger than max packet' , function ( done ) { request ( { port : port } ) . on ( 'response' , ( res ) => { let blockwiseResponse = false ; for ( const i in res . options ) { if ( res . options [ i ] . name === 'Block2' ) { blockwiseResponse = true ; break ; } } expect ( blockwiseResponse ) . to . eql ( true ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; it ( 'should blockwise response have etag' , function ( done ) { request ( { port : port } ) . on ( 'response' , ( res ) => { expect ( typeof res . headers . ETag ) . to . eql ( 'string' ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; it ( 'should accept early negotation' , function ( done ) { request ( { port : port } ) . setOption ( 'Block2' , Buffer . of ( 0x02 ) ) . on ( 'response' , ( res ) => { let block2 ; for ( const i in res . options ) { if ( res . options [ i ] . name === 'Block2' ) { block2 = res . options [ i ] . value ; break ; } } expect ( block2 instanceof Buffer ) . to . eql ( true ) ; expect ( block2 [ block2 . length - 1 ] & 0x07 ) . to . eql ( 2 ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; it ( 'should receive error when early negotation request block size higher than 1024' , function ( done ) { request ( { port : port } ) . setOption ( 'Block2' , Buffer . of ( 0x07 ) ) . on ( 'response' , ( res ) => { expect ( res . code ) . to . eql ( '4.02' ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; it ( 'should receive error request for out of range block number' , function ( done ) { request ( { port : port } ) . setOption ( 'Block2' , Buffer . of ( 0x3D ) ) . on ( 'response' , ( res ) => { expect ( res . code ) . to . eql ( '4.02' ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; it ( 'should be able to receive part of message' , function ( done ) { request ( { port : port } ) . setOption ( 'Block2' , Buffer . of ( 0x10 ) ) . on ( 'response' , ( res ) => { expect ( res . payload ) . to . eql ( payload . slice ( 1 * 16 , payload . length + 1 ) ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; it ( 'should receive full response payload' , function ( done ) { const payload = Buffer . alloc ( 16 * 0xff + 1 ) ; request ( { port : port } ) . setOption ( 'Block2' , Buffer . of ( 0x0 ) ) . on ( 'response' , ( res ) => { expect ( res . payload ) . to . eql ( payload ) ; setImmediate ( done ) ; } ) . end ( ) ; server . on ( 'request' , ( req , res ) => { res . end ( payload ) ; } ) ; } ) ; function sendNextBlock2 ( reqToken , reqBlock2Num ) { const packet = { messageId : 1100 + reqBlock2Num , token : reqToken , options : [ { name : 'Block2' , value : Buffer . of ( reqBlock2Num << 4 ) } ] } ; send ( generate ( packet ) ) ; } function parallelBlock2Test ( done , checkNReq , checkBlock2Message , checkNormalReq ) { const payloadLength = 32 + 16 + 1 ; const payloadReq1 = Buffer . alloc ( payloadLength ) ; const payloadReq2 = Buffer . alloc ( payloadLength ) ; const req1Token = Buffer . alloc ( 4 ) ; let req1Done = false ; let req2Done = false ; let req1Block2Num = 0 ; const reqClient2 = request ( { port : port } ) ; fillPayloadBuffer ( payloadReq1 ) ; fillPayloadBuffer ( payloadReq2 ) ; fillPayloadBuffer ( req1Token ) ; let nreq = 1 ; server . on ( 'request' , ( req , res ) => { if ( nreq === 1 ) { res . end ( payloadReq1 ) ; } else if ( nreq === 2 ) { res . end ( payloadReq2 ) ; } checkNReq ( nreq ) ; nreq ++ ; } ) ; sendNextBlock2 ( req1Token , req1Block2Num ) ; client . on ( 'message' , ( msg , rinfo ) => { checkBlock2Message ( msg , payloadReq1 , req1Block2Num , payloadLength ) ; const expectMore = ( req1Block2Num + 1 ) * 16 <= payloadLength ; if ( expectMore ) { req1Block2Num ++ ; setTimeout ( ( ) => { sendNextBlock2 ( req1Token , req1Block2Num ) ; } , 50 ) ; } else { req1Done = true ; if ( req1Done && req2Done ) { setImmediate ( done ) ; } } } ) ; reqClient2 . setOption ( 'Block2' , Buffer . of ( 0x10 ) ) ; setTimeout ( ( ) => { reqClient2 . end ( ) ; } , 1 ) ; reqClient2 . on ( 'response' , ( res ) => { checkNormalReq ( res , payloadReq2 ) ; req2Done = true ; if ( req1Done && req2Done ) { setImmediate ( done ) ; } } ) ; } function checkNothing ( ) { } it ( 'should two parallel block2 requests should result only two requests to upper level' , function ( done ) { const checkNreq = ( nreq ) : void => { expect ( nreq ) . to . be . within ( 1 , 2 ) ; } ; parallelBlock2Test ( done , checkNreq , checkNothing , checkNothing ) ; } ) ; it ( 'should have code 2.05 for all block2 messages of successful parallel requests' , function ( done ) { const checkBlock2Code = ( msg ) : void => { const res = parse ( msg ) ; expect ( res . code ) . to . eql ( '2.05' ) ; } ; const checkNormalRespCode = ( res ) : void => { expect ( res . code ) . to . eql ( '2.05' ) ; } ; parallelBlock2Test ( done , checkNothing , checkBlock2Code , checkNormalRespCode ) ; } ) ; it ( 'should have correct block2 option for parallel requests' , function ( done ) { const checkBlock2Option = ( msg , payloadReq1 , req1Block2Num , payloadLength ) : void => { const res = parse ( msg ) ; const block2Buff = getOption ( res . options , 'Block2' ) ; if ( block2Buff instanceof Buffer ) { const block2 = parseBlock2 ( block2Buff ) ; expect ( block2 ) . to . not . eql ( null ) ; const expectMore = ( req1Block2Num + 1 ) * 16 <= payloadLength ? 1 : 0 ; if ( block2 != null ) { expect ( block2 . num ) . to . eql ( req1Block2Num ) ; expect ( block2 . more ) . to . eql ( expectMore ) ; } else { done ( new Error ( 'parseBlock2 returned an invalid Block option!' ) ) ; } } else { done ( new Error ( 'getOption did not return a Buffer!' ) ) ; } } ; parallelBlock2Test ( done , checkNothing , checkBlock2Option , checkNothing ) ; } ) ; it ( 'should have correct payload in block2 messages for parallel requests' , function ( done ) { const checkBlock2Payload = ( msg , payloadReq1 , req1Block2Num ) : void => { const res = parse ( msg ) ; expect ( res . payload ) . to . eql ( payloadReq1 . slice ( req1Block2Num * 16 , req1Block2Num * 16 + 16 ) ) ; } ; const checkNormalRespPayload = ( res , payloadReq2 ) : void => { expect ( res . payload ) . to . eql ( payloadReq2 . slice ( 1 * 16 , payload . length + 1 ) ) ; } ; parallelBlock2Test ( done , checkNothing , checkBlock2Payload , checkNormalRespPayload ) ; } ) ; } ) ; describe ( 'blockwise1' , ( ) => { describe ( 'Generate Block Options' , ( ) => { it ( 'it should return buffer' , ( done ) => { const payload = Buffer . of ( 0x01 ) ; const value = generateBlockOption ( 0 , 0 , 1 ) ; expect ( payload ) . to . eql ( value ) ; setImmediate ( done ) ; } ) ; it ( 'it should return buffer equal to 1,0,1' , ( done ) => { const payload = Buffer . of ( 0x01 , 0x00 , 0x01 ) ; const value = generateBlockOption ( 4096 , 0 , 1 ) ; expect ( payload ) . to . eql ( value ) ; setImmediate ( done ) ; } ) ; it ( 'it should return buffer equal to 1,1' , ( done ) => { const payload = Buffer . of ( 0x01 , 0x01 ) ; const value = generateBlockOption ( 16 , 0 , 1 ) ; expect ( payload ) . to . eql ( value ) ; setImmediate ( done ) ; } ) ; it ( 'it should throw Invalid Parameters error' , ( done ) => { expect ( ( ) => { generateBlockOption ( 0 , 0 , undefined ) ; } ) . to . throw ( 'Invalid parameters' ) ; setImmediate ( done ) ; } ) ; it ( 'it should throw Sequence error' , ( done ) => { expect ( ( ) => { generateBlockOption ( 1048576 , 0 , 0 ) ; } ) . to . throw ( 'Sequence number out of range' ) ; setImmediate ( done ) ; } ) ; } ) ; describe ( 'Parse Block Options' , ( ) => { it ( 'it should return object' , ( done ) => { const payload = Buffer . of ( 0x01 ) ; const response = { num : 0 , more : 0 , size : 1 } ; const value = parseBlockOption ( payload ) ; expect ( value ) . to . eql ( response ) ; setImmediate ( done ) ; } ) ; it ( 'it should return object when length is equal to 2' , ( done ) => { const payload = Buffer . of ( 0x01 , 0x02 ) ; const response = { num : 16 , more : 0 , size : 2 } ; const value = parseBlockOption ( payload ) ; expect ( value ) . to . eql ( response ) ; setImmediate ( done ) ; } ) ; it ( 'it should return object when length is equal to 3' , ( done ) => { const payload = Buffer . of ( 0x01 , 0x02 , 0x03 ) ; const response = { num : 4128 , more : 0 , size : 3 } ; const value = parseBlockOption ( payload ) ; expect ( value ) . to . eql ( response ) ; setImmediate ( done ) ; } ) ; it ( 'it should throw Invalid Block Option error' , ( done ) => { const payload = Buffer . from ( [ 0x04 , 0x01 , 0x03 , 0x04 ] ) ; expect ( ( ) => { parseBlockOption ( payload ) ; } ) . to . throw ( 'Invalid block option buffer length. Must be 1, 2 or 3. It is 4' ) ; setImmediate ( done ) ; } ) ; } ) ; describe ( 'Exponenent to Byte Size' , ( ) => { it ( 'it should return value' , ( done ) => { const response = 1024 ; const payload = 6 ; const value = exponentToByteSize ( payload ) ; expect ( value ) . to . eql ( response ) ; setImmediate ( done ) ; } ) ; } ) ; describe ( 'Byte Size to Exponenet' , ( ) => { it ( 'it should return value' , ( done ) => { const response = 1024 ; const payload = 6 ; const value = byteSizeToExponent ( response ) ; expect ( value ) . to . eql ( payload ) ; setImmediate ( done ) ; } ) ; } ) ; } ) ;