test.js 66 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845
  1. const util = require('util')
  2. const test = require('tape')
  3. const mqtt = require('./')
  4. const WS = require('readable-stream').Writable
  5. function normalExpectedObject (object) {
  6. if (object.username != null) object.username = object.username.toString()
  7. if (object.password != null) object.password = Buffer.from(object.password)
  8. return object
  9. }
  10. function testParseGenerate (name, object, buffer, opts) {
  11. test(`${name} parse`, t => {
  12. t.plan(2)
  13. const parser = mqtt.parser(opts)
  14. const expected = object
  15. const fixture = buffer
  16. parser.on('packet', packet => {
  17. if (packet.cmd !== 'publish') {
  18. delete packet.topic
  19. delete packet.payload
  20. }
  21. t.deepLooseEqual(packet, normalExpectedObject(expected), 'expected packet')
  22. })
  23. parser.on('error', err => {
  24. t.fail(err)
  25. })
  26. t.equal(parser.parse(fixture), 0, 'remaining bytes')
  27. })
  28. test(`${name} generate`, t => {
  29. // For really large buffers, the expanded hex string can be so long as to
  30. // generate an error in nodejs 14.x, so only do the test with extra output
  31. // for relatively small buffers.
  32. const bigLength = 10000
  33. const generatedBuffer = mqtt.generate(object, opts)
  34. if (generatedBuffer.length < bigLength && buffer.length < bigLength) {
  35. t.equal(generatedBuffer.toString('hex'), buffer.toString('hex'))
  36. } else {
  37. const bufferOkay = generatedBuffer.equals(buffer)
  38. if (bufferOkay) {
  39. t.pass()
  40. } else {
  41. // Output abbreviated representations of the buffers.
  42. t.comment('Expected:\n' + util.inspect(buffer))
  43. t.comment('Got:\n' + util.inspect(generatedBuffer))
  44. t.fail('Large buffers not equal')
  45. }
  46. }
  47. t.end()
  48. })
  49. test(`${name} mirror`, t => {
  50. t.plan(2)
  51. const parser = mqtt.parser(opts)
  52. const expected = object
  53. const fixture = mqtt.generate(object, opts)
  54. parser.on('packet', packet => {
  55. if (packet.cmd !== 'publish') {
  56. delete packet.topic
  57. delete packet.payload
  58. }
  59. t.deepLooseEqual(packet, normalExpectedObject(expected), 'expected packet')
  60. })
  61. parser.on('error', err => {
  62. t.fail(err)
  63. })
  64. t.equal(parser.parse(fixture), 0, 'remaining bytes')
  65. })
  66. test(`${name} writeToStream`, t => {
  67. const stream = WS()
  68. stream.write = () => true
  69. stream.on('error', (err) => t.fail(err))
  70. const result = mqtt.writeToStream(object, stream, opts)
  71. t.equal(result, true, 'should return true')
  72. t.end()
  73. })
  74. }
  75. // the API allows to pass strings as buffers to writeToStream and generate
  76. // parsing them back will result in a string so only generate and compare to buffer
  77. function testGenerateOnly (name, object, buffer, opts) {
  78. test(name, t => {
  79. t.equal(mqtt.generate(object, opts).toString('hex'), buffer.toString('hex'))
  80. t.end()
  81. })
  82. }
  83. function testParseOnly (name, object, buffer, opts) {
  84. test(name, t => {
  85. const parser = mqtt.parser(opts)
  86. // const expected = object
  87. // const fixture = buffer
  88. t.plan(2 + Object.keys(object).length)
  89. parser.on('packet', packet => {
  90. t.equal(Object.keys(object).length, Object.keys(packet).length, 'key count')
  91. Object.keys(object).forEach(key => {
  92. t.deepEqual(packet[key], object[key], `expected packet property ${key}`)
  93. })
  94. })
  95. t.equal(parser.parse(buffer), 0, 'remaining bytes')
  96. t.end()
  97. })
  98. }
  99. function testParseError (expected, fixture, opts) {
  100. test(expected, t => {
  101. t.plan(1)
  102. const parser = mqtt.parser(opts)
  103. parser.on('error', err => {
  104. t.equal(err.message, expected, 'expected error message')
  105. })
  106. parser.on('packet', () => {
  107. t.fail('parse errors should not be followed by packet events')
  108. })
  109. parser.parse(fixture)
  110. t.end()
  111. })
  112. }
  113. function testGenerateError (expected, fixture, opts, name) {
  114. test(name || expected, t => {
  115. t.plan(1)
  116. try {
  117. mqtt.generate(fixture, opts)
  118. } catch (err) {
  119. t.equal(expected, err.message)
  120. }
  121. t.end()
  122. })
  123. }
  124. function testGenerateErrorMultipleCmds (cmds, expected, fixture, opts) {
  125. cmds.forEach(cmd => {
  126. const obj = Object.assign({}, fixture)
  127. obj.cmd = cmd
  128. testGenerateError(expected, obj, opts, `${expected} on ${cmd}`)
  129. }
  130. )
  131. }
  132. function testParseGenerateDefaults (name, object, buffer, generated, opts) {
  133. testParseOnly(`${name} parse`, generated, buffer, opts)
  134. testGenerateOnly(`${name} generate`, object, buffer, opts)
  135. }
  136. function testParseAndGenerate (name, object, buffer, opts) {
  137. testParseOnly(`${name} parse`, object, buffer, opts)
  138. testGenerateOnly(`${name} generate`, object, buffer, opts)
  139. }
  140. function testWriteToStreamError (expected, fixture) {
  141. test(`writeToStream ${expected} error`, t => {
  142. t.plan(2)
  143. const stream = WS()
  144. stream.write = () => t.fail('should not have called write')
  145. stream.on('error', () => t.pass('error emitted'))
  146. const result = mqtt.writeToStream(fixture, stream)
  147. t.false(result, 'result should be false')
  148. t.end()
  149. })
  150. }
  151. test('cacheNumbers get/set/unset', t => {
  152. t.true(mqtt.writeToStream.cacheNumbers, 'initial state of cacheNumbers is enabled')
  153. mqtt.writeToStream.cacheNumbers = false
  154. t.false(mqtt.writeToStream.cacheNumbers, 'cacheNumbers can be disabled')
  155. mqtt.writeToStream.cacheNumbers = true
  156. t.true(mqtt.writeToStream.cacheNumbers, 'cacheNumbers can be enabled')
  157. t.end()
  158. })
  159. test('disabled numbers cache', t => {
  160. const stream = WS()
  161. const message = {
  162. cmd: 'publish',
  163. retain: false,
  164. qos: 0,
  165. dup: false,
  166. length: 10,
  167. topic: Buffer.from('test'),
  168. payload: Buffer.from('test')
  169. }
  170. const expected = Buffer.from([
  171. 48, 10, // Header
  172. 0, 4, // Topic length
  173. 116, 101, 115, 116, // Topic (test)
  174. 116, 101, 115, 116 // Payload (test)
  175. ])
  176. let written = Buffer.alloc(0)
  177. stream.write = (chunk) => {
  178. written = Buffer.concat([written, chunk])
  179. }
  180. mqtt.writeToStream.cacheNumbers = false
  181. mqtt.writeToStream(message, stream)
  182. t.deepEqual(written, expected, 'written buffer is expected')
  183. mqtt.writeToStream.cacheNumbers = true
  184. stream.end()
  185. t.end()
  186. })
  187. testGenerateError('Unknown command', {})
  188. testParseError('Not supported', Buffer.from([0, 1, 0]), {})
  189. // Length header field
  190. testParseError('Invalid variable byte integer', Buffer.from(
  191. [16, 255, 255, 255, 255]
  192. ), {})
  193. testParseError('Invalid variable byte integer', Buffer.from(
  194. [16, 255, 255, 255, 128]
  195. ), {})
  196. testParseError('Invalid variable byte integer', Buffer.from(
  197. [16, 255, 255, 255, 255, 1]
  198. ), {})
  199. testParseError('Invalid variable byte integer', Buffer.from(
  200. [16, 255, 255, 255, 255, 127]
  201. ), {})
  202. testParseError('Invalid variable byte integer', Buffer.from(
  203. [16, 255, 255, 255, 255, 128]
  204. ), {})
  205. testParseError('Invalid variable byte integer', Buffer.from(
  206. [16, 255, 255, 255, 255, 255, 1]
  207. ), {})
  208. testParseGenerate('minimal connect', {
  209. cmd: 'connect',
  210. retain: false,
  211. qos: 0,
  212. dup: false,
  213. length: 18,
  214. protocolId: 'MQIsdp',
  215. protocolVersion: 3,
  216. clean: false,
  217. keepalive: 30,
  218. clientId: 'test'
  219. }, Buffer.from([
  220. 16, 18, // Header
  221. 0, 6, // Protocol ID length
  222. 77, 81, 73, 115, 100, 112, // Protocol ID
  223. 3, // Protocol version
  224. 0, // Connect flags
  225. 0, 30, // Keepalive
  226. 0, 4, // Client ID length
  227. 116, 101, 115, 116 // Client ID
  228. ]))
  229. testGenerateOnly('minimal connect with clientId as Buffer', {
  230. cmd: 'connect',
  231. retain: false,
  232. qos: 0,
  233. dup: false,
  234. length: 18,
  235. protocolId: 'MQIsdp',
  236. protocolVersion: 3,
  237. clean: false,
  238. keepalive: 30,
  239. clientId: Buffer.from('test')
  240. }, Buffer.from([
  241. 16, 18, // Header
  242. 0, 6, // Protocol ID length
  243. 77, 81, 73, 115, 100, 112, // Protocol ID
  244. 3, // Protocol version
  245. 0, // Connect flags
  246. 0, 30, // Keepalive
  247. 0, 4, // Client ID length
  248. 116, 101, 115, 116 // Client ID
  249. ]))
  250. testParseGenerate('connect MQTT bridge 131', {
  251. cmd: 'connect',
  252. retain: false,
  253. qos: 0,
  254. dup: false,
  255. length: 18,
  256. protocolId: 'MQIsdp',
  257. protocolVersion: 3,
  258. bridgeMode: true,
  259. clean: false,
  260. keepalive: 30,
  261. clientId: 'test'
  262. }, Buffer.from([
  263. 16, 18, // Header
  264. 0, 6, // Protocol ID length
  265. 77, 81, 73, 115, 100, 112, // Protocol ID
  266. 131, // Protocol version
  267. 0, // Connect flags
  268. 0, 30, // Keepalive
  269. 0, 4, // Client ID length
  270. 116, 101, 115, 116 // Client ID
  271. ]))
  272. testParseGenerate('connect MQTT bridge 132', {
  273. cmd: 'connect',
  274. retain: false,
  275. qos: 0,
  276. dup: false,
  277. length: 18,
  278. protocolId: 'MQIsdp',
  279. protocolVersion: 4,
  280. bridgeMode: true,
  281. clean: false,
  282. keepalive: 30,
  283. clientId: 'test'
  284. }, Buffer.from([
  285. 16, 18, // Header
  286. 0, 6, // Protocol ID length
  287. 77, 81, 73, 115, 100, 112, // Protocol ID
  288. 132, // Protocol version
  289. 0, // Connect flags
  290. 0, 30, // Keepalive
  291. 0, 4, // Client ID length
  292. 116, 101, 115, 116 // Client ID
  293. ]))
  294. testParseGenerate('connect MQTT 5', {
  295. cmd: 'connect',
  296. retain: false,
  297. qos: 0,
  298. dup: false,
  299. length: 125,
  300. protocolId: 'MQTT',
  301. protocolVersion: 5,
  302. will: {
  303. retain: true,
  304. qos: 2,
  305. properties: {
  306. willDelayInterval: 1234,
  307. payloadFormatIndicator: false,
  308. messageExpiryInterval: 4321,
  309. contentType: 'test',
  310. responseTopic: 'topic',
  311. correlationData: Buffer.from([1, 2, 3, 4]),
  312. userProperties: {
  313. test: 'test'
  314. }
  315. },
  316. topic: 'topic',
  317. payload: Buffer.from([4, 3, 2, 1])
  318. },
  319. clean: true,
  320. keepalive: 30,
  321. properties: {
  322. sessionExpiryInterval: 1234,
  323. receiveMaximum: 432,
  324. maximumPacketSize: 100,
  325. topicAliasMaximum: 456,
  326. requestResponseInformation: true,
  327. requestProblemInformation: true,
  328. userProperties: {
  329. test: 'test'
  330. },
  331. authenticationMethod: 'test',
  332. authenticationData: Buffer.from([1, 2, 3, 4])
  333. },
  334. clientId: 'test'
  335. }, Buffer.from([
  336. 16, 125, // Header
  337. 0, 4, // Protocol ID length
  338. 77, 81, 84, 84, // Protocol ID
  339. 5, // Protocol version
  340. 54, // Connect flags
  341. 0, 30, // Keepalive
  342. 47, // properties length
  343. 17, 0, 0, 4, 210, // sessionExpiryInterval
  344. 33, 1, 176, // receiveMaximum
  345. 39, 0, 0, 0, 100, // maximumPacketSize
  346. 34, 1, 200, // topicAliasMaximum
  347. 25, 1, // requestResponseInformation
  348. 23, 1, // requestProblemInformation,
  349. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties,
  350. 21, 0, 4, 116, 101, 115, 116, // authenticationMethod
  351. 22, 0, 4, 1, 2, 3, 4, // authenticationData
  352. 0, 4, // Client ID length
  353. 116, 101, 115, 116, // Client ID
  354. 47, // will properties
  355. 24, 0, 0, 4, 210, // will delay interval
  356. 1, 0, // payload format indicator
  357. 2, 0, 0, 16, 225, // message expiry interval
  358. 3, 0, 4, 116, 101, 115, 116, // content type
  359. 8, 0, 5, 116, 111, 112, 105, 99, // response topic
  360. 9, 0, 4, 1, 2, 3, 4, // corelation data
  361. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // user properties
  362. 0, 5, // Will topic length
  363. 116, 111, 112, 105, 99, // Will topic
  364. 0, 4, // Will payload length
  365. 4, 3, 2, 1// Will payload
  366. ]))
  367. testParseGenerate('connect MQTT 5 with will properties but with empty will payload', {
  368. cmd: 'connect',
  369. retain: false,
  370. qos: 0,
  371. dup: false,
  372. length: 121,
  373. protocolId: 'MQTT',
  374. protocolVersion: 5,
  375. will: {
  376. retain: true,
  377. qos: 2,
  378. properties: {
  379. willDelayInterval: 1234,
  380. payloadFormatIndicator: false,
  381. messageExpiryInterval: 4321,
  382. contentType: 'test',
  383. responseTopic: 'topic',
  384. correlationData: Buffer.from([1, 2, 3, 4]),
  385. userProperties: {
  386. test: 'test'
  387. }
  388. },
  389. topic: 'topic',
  390. payload: Buffer.from([])
  391. },
  392. clean: true,
  393. keepalive: 30,
  394. properties: {
  395. sessionExpiryInterval: 1234,
  396. receiveMaximum: 432,
  397. maximumPacketSize: 100,
  398. topicAliasMaximum: 456,
  399. requestResponseInformation: true,
  400. requestProblemInformation: true,
  401. userProperties: {
  402. test: 'test'
  403. },
  404. authenticationMethod: 'test',
  405. authenticationData: Buffer.from([1, 2, 3, 4])
  406. },
  407. clientId: 'test'
  408. }, Buffer.from([
  409. 16, 121, // Header
  410. 0, 4, // Protocol ID length
  411. 77, 81, 84, 84, // Protocol ID
  412. 5, // Protocol version
  413. 54, // Connect flags
  414. 0, 30, // Keepalive
  415. 47, // properties length
  416. 17, 0, 0, 4, 210, // sessionExpiryInterval
  417. 33, 1, 176, // receiveMaximum
  418. 39, 0, 0, 0, 100, // maximumPacketSize
  419. 34, 1, 200, // topicAliasMaximum
  420. 25, 1, // requestResponseInformation
  421. 23, 1, // requestProblemInformation,
  422. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties,
  423. 21, 0, 4, 116, 101, 115, 116, // authenticationMethod
  424. 22, 0, 4, 1, 2, 3, 4, // authenticationData
  425. 0, 4, // Client ID length
  426. 116, 101, 115, 116, // Client ID
  427. 47, // will properties
  428. 24, 0, 0, 4, 210, // will delay interval
  429. 1, 0, // payload format indicator
  430. 2, 0, 0, 16, 225, // message expiry interval
  431. 3, 0, 4, 116, 101, 115, 116, // content type
  432. 8, 0, 5, 116, 111, 112, 105, 99, // response topic
  433. 9, 0, 4, 1, 2, 3, 4, // corelation data
  434. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // user properties
  435. 0, 5, // Will topic length
  436. 116, 111, 112, 105, 99, // Will topic
  437. 0, 0 // Will payload length
  438. ]))
  439. testParseGenerate('connect MQTT 5 w/o will properties', {
  440. cmd: 'connect',
  441. retain: false,
  442. qos: 0,
  443. dup: false,
  444. length: 78,
  445. protocolId: 'MQTT',
  446. protocolVersion: 5,
  447. will: {
  448. retain: true,
  449. qos: 2,
  450. topic: 'topic',
  451. payload: Buffer.from([4, 3, 2, 1])
  452. },
  453. clean: true,
  454. keepalive: 30,
  455. properties: {
  456. sessionExpiryInterval: 1234,
  457. receiveMaximum: 432,
  458. maximumPacketSize: 100,
  459. topicAliasMaximum: 456,
  460. requestResponseInformation: true,
  461. requestProblemInformation: true,
  462. userProperties: {
  463. test: 'test'
  464. },
  465. authenticationMethod: 'test',
  466. authenticationData: Buffer.from([1, 2, 3, 4])
  467. },
  468. clientId: 'test'
  469. }, Buffer.from([
  470. 16, 78, // Header
  471. 0, 4, // Protocol ID length
  472. 77, 81, 84, 84, // Protocol ID
  473. 5, // Protocol version
  474. 54, // Connect flags
  475. 0, 30, // Keepalive
  476. 47, // properties length
  477. 17, 0, 0, 4, 210, // sessionExpiryInterval
  478. 33, 1, 176, // receiveMaximum
  479. 39, 0, 0, 0, 100, // maximumPacketSize
  480. 34, 1, 200, // topicAliasMaximum
  481. 25, 1, // requestResponseInformation
  482. 23, 1, // requestProblemInformation,
  483. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties,
  484. 21, 0, 4, 116, 101, 115, 116, // authenticationMethod
  485. 22, 0, 4, 1, 2, 3, 4, // authenticationData
  486. 0, 4, // Client ID length
  487. 116, 101, 115, 116, // Client ID
  488. 0, // will properties
  489. 0, 5, // Will topic length
  490. 116, 111, 112, 105, 99, // Will topic
  491. 0, 4, // Will payload length
  492. 4, 3, 2, 1// Will payload
  493. ]))
  494. testParseGenerate('no clientId with 3.1.1', {
  495. cmd: 'connect',
  496. retain: false,
  497. qos: 0,
  498. dup: false,
  499. length: 12,
  500. protocolId: 'MQTT',
  501. protocolVersion: 4,
  502. clean: true,
  503. keepalive: 30,
  504. clientId: ''
  505. }, Buffer.from([
  506. 16, 12, // Header
  507. 0, 4, // Protocol ID length
  508. 77, 81, 84, 84, // Protocol ID
  509. 4, // Protocol version
  510. 2, // Connect flags
  511. 0, 30, // Keepalive
  512. 0, 0 // Client ID length
  513. ]))
  514. testParseGenerateDefaults('no clientId with 5.0', {
  515. cmd: 'connect',
  516. protocolId: 'MQTT',
  517. protocolVersion: 5,
  518. clean: true,
  519. keepalive: 60,
  520. properties:
  521. {
  522. receiveMaximum: 20
  523. },
  524. clientId: ''
  525. }, Buffer.from(
  526. [16, 16, 0, 4, 77, 81, 84, 84, 5, 2, 0, 60, 3, 33, 0, 20, 0, 0]
  527. ), {
  528. cmd: 'connect',
  529. retain: false,
  530. qos: 0,
  531. dup: false,
  532. length: 16,
  533. topic: null,
  534. payload: null,
  535. protocolId: 'MQTT',
  536. protocolVersion: 5,
  537. clean: true,
  538. keepalive: 60,
  539. properties: {
  540. receiveMaximum: 20
  541. },
  542. clientId: ''
  543. }, { protocolVersion: 5 })
  544. testParseGenerateDefaults('utf-8 clientId with 5.0', {
  545. cmd: 'connect',
  546. retain: false,
  547. qos: 0,
  548. dup: false,
  549. length: 23,
  550. protocolId: 'MQTT',
  551. protocolVersion: 4,
  552. clean: true,
  553. keepalive: 30,
  554. clientId: 'Ŧėśt🜄'
  555. }, Buffer.from([
  556. 16, 23, // Header
  557. 0, 4, // Protocol ID length
  558. 77, 81, 84, 84, // Protocol ID
  559. 4, // Protocol version
  560. 2, // Connect flags
  561. 0, 30, // Keepalive
  562. 0, 11, // Client ID length
  563. 197, 166, // Ŧ (UTF-8: 0xc5a6)
  564. 196, 151, // ė (UTF-8: 0xc497)
  565. 197, 155, // ś (utf-8: 0xc59b)
  566. 116, // t (utf-8: 0x74)
  567. 240, 159, 156, 132 // 🜄 (utf-8: 0xf09f9c84)
  568. ]), {
  569. cmd: 'connect',
  570. retain: false,
  571. qos: 0,
  572. dup: false,
  573. length: 23,
  574. topic: null,
  575. payload: null,
  576. protocolId: 'MQTT',
  577. protocolVersion: 4,
  578. clean: true,
  579. keepalive: 30,
  580. clientId: 'Ŧėśt🜄'
  581. }, { protocol: 5 })
  582. testParseGenerateDefaults('default connect', {
  583. cmd: 'connect',
  584. clientId: 'test'
  585. }, Buffer.from([
  586. 16, 16, 0, 4, 77, 81, 84,
  587. 84, 4, 2, 0, 0,
  588. 0, 4, 116, 101, 115, 116
  589. ]), {
  590. cmd: 'connect',
  591. retain: false,
  592. qos: 0,
  593. dup: false,
  594. length: 16,
  595. topic: null,
  596. payload: null,
  597. protocolId: 'MQTT',
  598. protocolVersion: 4,
  599. clean: true,
  600. keepalive: 0,
  601. clientId: 'test'
  602. })
  603. testParseAndGenerate('Version 4 CONACK', {
  604. cmd: 'connack',
  605. retain: false,
  606. qos: 0,
  607. dup: false,
  608. length: 2,
  609. topic: null,
  610. payload: null,
  611. sessionPresent: false,
  612. returnCode: 1
  613. }, Buffer.from([
  614. 32, 2, // Fixed Header (CONNACK, Remaining Length)
  615. 0, 1 // Variable Header (Session not present, Connection Refused - unacceptable protocol version)
  616. ]), {}) // Default protocolVersion (4)
  617. testParseAndGenerate('Version 5 CONACK', {
  618. cmd: 'connack',
  619. retain: false,
  620. qos: 0,
  621. dup: false,
  622. length: 3,
  623. topic: null,
  624. payload: null,
  625. sessionPresent: false,
  626. reasonCode: 140
  627. }, Buffer.from([
  628. 32, 3, // Fixed Header (CONNACK, Remaining Length)
  629. 0, 140, // Variable Header (Session not present, Bad authentication method)
  630. 0 // Property Length Zero
  631. ]), { protocolVersion: 5 })
  632. testParseOnly('Version 4 CONACK in Version 5 mode', {
  633. cmd: 'connack',
  634. retain: false,
  635. qos: 0,
  636. dup: false,
  637. length: 2,
  638. topic: null,
  639. payload: null,
  640. sessionPresent: false,
  641. reasonCode: 1 // a version 4 return code stored in the version 5 reasonCode because this client is in version 5
  642. }, Buffer.from([
  643. 32, 2, // Fixed Header (CONNACK, Remaining Length)
  644. 0, 1 // Variable Header (Session not present, Connection Refused - unacceptable protocol version)
  645. ]), { protocolVersion: 5 }) // message is in version 4 format, but this client is in version 5 mode
  646. testParseOnly('Version 5 PUBACK test 1', {
  647. cmd: 'puback',
  648. messageId: 42,
  649. retain: false,
  650. qos: 0,
  651. dup: false,
  652. length: 2,
  653. topic: null,
  654. payload: null,
  655. reasonCode: 0
  656. }, Buffer.from([
  657. 64, 2, // Fixed Header (PUBACK, Remaining Length)
  658. 0, 42 // Variable Header (2 Bytes: Packet Identifier 42, Implied Reason code: Success, Implied no properties)
  659. ]), { protocolVersion: 5 }
  660. )
  661. testParseAndGenerate('Version 5 PUBACK test 2', {
  662. cmd: 'puback',
  663. messageId: 42,
  664. retain: false,
  665. qos: 0,
  666. dup: false,
  667. length: 3,
  668. topic: null,
  669. payload: null,
  670. reasonCode: 0
  671. }, Buffer.from([
  672. 64, 3, // Fixed Header (PUBACK, Remaining Length)
  673. 0, 42, 0 // Variable Header (2 Bytes: Packet Identifier 42, Reason code: 0 Success, Implied no properties)
  674. ]), { protocolVersion: 5 }
  675. )
  676. testParseOnly('Version 5 PUBACK test 3', {
  677. cmd: 'puback',
  678. messageId: 42,
  679. retain: false,
  680. qos: 0,
  681. dup: false,
  682. length: 4,
  683. topic: null,
  684. payload: null,
  685. reasonCode: 0
  686. }, Buffer.from([
  687. 64, 4, // Fixed Header (PUBACK, Remaining Length)
  688. 0, 42, 0, // Variable Header (2 Bytes: Packet Identifier 42, Reason code: 0 Success)
  689. 0 // no properties
  690. ]), { protocolVersion: 5 }
  691. )
  692. testParseOnly('Version 5 CONNACK test 1', {
  693. cmd: 'connack',
  694. retain: false,
  695. qos: 0,
  696. dup: false,
  697. length: 1,
  698. topic: null,
  699. payload: null,
  700. sessionPresent: true,
  701. reasonCode: 0
  702. }, Buffer.from([
  703. 32, 1, // Fixed Header (CONNACK, Remaining Length)
  704. 1 // Variable Header (Session Present: 1 => true, Implied Reason code: Success, Implied no properties)
  705. ]), { protocolVersion: 5 }
  706. )
  707. testParseOnly('Version 5 CONNACK test 2', {
  708. cmd: 'connack',
  709. retain: false,
  710. qos: 0,
  711. dup: false,
  712. length: 2,
  713. topic: null,
  714. payload: null,
  715. sessionPresent: true,
  716. reasonCode: 0
  717. }, Buffer.from([
  718. 32, 2, // Fixed Header (CONNACK, Remaining Length)
  719. 1, 0 // Variable Header (Session Present: 1 => true, Connect Reason code: Success, Implied no properties)
  720. ]), { protocolVersion: 5 }
  721. )
  722. testParseAndGenerate('Version 5 CONNACK test 3', {
  723. cmd: 'connack',
  724. retain: false,
  725. qos: 0,
  726. dup: false,
  727. length: 3,
  728. topic: null,
  729. payload: null,
  730. sessionPresent: true,
  731. reasonCode: 0
  732. }, Buffer.from([
  733. 32, 3, // Fixed Header (CONNACK, Remaining Length)
  734. 1, 0, // Variable Header (Session Present: 1 => true, Connect Reason code: Success)
  735. 0 // no properties
  736. ]), { protocolVersion: 5 }
  737. )
  738. testParseOnly('Version 5 DISCONNECT test 1', {
  739. cmd: 'disconnect',
  740. retain: false,
  741. qos: 0,
  742. dup: false,
  743. length: 0,
  744. topic: null,
  745. payload: null,
  746. reasonCode: 0
  747. }, Buffer.from([
  748. 224, 0 // Fixed Header (DISCONNECT, Remaining Length), Implied Reason code: Normal Disconnection
  749. ]), { protocolVersion: 5 }
  750. )
  751. testParseOnly('Version 5 DISCONNECT test 2', {
  752. cmd: 'disconnect',
  753. retain: false,
  754. qos: 0,
  755. dup: false,
  756. length: 1,
  757. topic: null,
  758. payload: null,
  759. reasonCode: 0
  760. }, Buffer.from([
  761. 224, 1, // Fixed Header (DISCONNECT, Remaining Length)
  762. 0 // reason Code (Normal disconnection)
  763. ]), { protocolVersion: 5 }
  764. )
  765. testParseAndGenerate('Version 5 DISCONNECT test 3', {
  766. cmd: 'disconnect',
  767. retain: false,
  768. qos: 0,
  769. dup: false,
  770. length: 2,
  771. topic: null,
  772. payload: null,
  773. reasonCode: 0
  774. }, Buffer.from([
  775. 224, 2, // Fixed Header (DISCONNECT, Remaining Length)
  776. 0, // reason Code (Normal disconnection)
  777. 0 // no properties
  778. ]), { protocolVersion: 5 }
  779. )
  780. testParseGenerate('empty will payload', {
  781. cmd: 'connect',
  782. retain: false,
  783. qos: 0,
  784. dup: false,
  785. length: 47,
  786. protocolId: 'MQIsdp',
  787. protocolVersion: 3,
  788. will: {
  789. retain: true,
  790. qos: 2,
  791. topic: 'topic',
  792. payload: Buffer.alloc(0)
  793. },
  794. clean: true,
  795. keepalive: 30,
  796. clientId: 'test',
  797. username: 'username',
  798. password: Buffer.from('password')
  799. }, Buffer.from([
  800. 16, 47, // Header
  801. 0, 6, // Protocol ID length
  802. 77, 81, 73, 115, 100, 112, // Protocol ID
  803. 3, // Protocol version
  804. 246, // Connect flags
  805. 0, 30, // Keepalive
  806. 0, 4, // Client ID length
  807. 116, 101, 115, 116, // Client ID
  808. 0, 5, // Will topic length
  809. 116, 111, 112, 105, 99, // Will topic
  810. 0, 0, // Will payload length
  811. // Will payload
  812. 0, 8, // Username length
  813. 117, 115, 101, 114, 110, 97, 109, 101, // Username
  814. 0, 8, // Password length
  815. 112, 97, 115, 115, 119, 111, 114, 100 // Password
  816. ]))
  817. testParseGenerate('empty buffer username payload', {
  818. cmd: 'connect',
  819. retain: false,
  820. qos: 0,
  821. dup: false,
  822. length: 20,
  823. protocolId: 'MQIsdp',
  824. protocolVersion: 3,
  825. clean: true,
  826. keepalive: 30,
  827. clientId: 'test',
  828. username: Buffer.from('')
  829. }, Buffer.from([
  830. 16, 20, // Header
  831. 0, 6, // Protocol ID length
  832. 77, 81, 73, 115, 100, 112, // Protocol ID
  833. 3, // Protocol version
  834. 130, // Connect flags
  835. 0, 30, // Keepalive
  836. 0, 4, // Client ID length
  837. 116, 101, 115, 116, // Client ID
  838. 0, 0 // Username length
  839. // Empty Username payload
  840. ]))
  841. testParseGenerate('empty string username payload', {
  842. cmd: 'connect',
  843. retain: false,
  844. qos: 0,
  845. dup: false,
  846. length: 20,
  847. protocolId: 'MQIsdp',
  848. protocolVersion: 3,
  849. clean: true,
  850. keepalive: 30,
  851. clientId: 'test',
  852. username: ''
  853. }, Buffer.from([
  854. 16, 20, // Header
  855. 0, 6, // Protocol ID length
  856. 77, 81, 73, 115, 100, 112, // Protocol ID
  857. 3, // Protocol version
  858. 130, // Connect flags
  859. 0, 30, // Keepalive
  860. 0, 4, // Client ID length
  861. 116, 101, 115, 116, // Client ID
  862. 0, 0 // Username length
  863. // Empty Username payload
  864. ]))
  865. testParseGenerate('empty buffer password payload', {
  866. cmd: 'connect',
  867. retain: false,
  868. qos: 0,
  869. dup: false,
  870. length: 30,
  871. protocolId: 'MQIsdp',
  872. protocolVersion: 3,
  873. clean: true,
  874. keepalive: 30,
  875. clientId: 'test',
  876. username: 'username',
  877. password: Buffer.from('')
  878. }, Buffer.from([
  879. 16, 30, // Header
  880. 0, 6, // Protocol ID length
  881. 77, 81, 73, 115, 100, 112, // Protocol ID
  882. 3, // Protocol version
  883. 194, // Connect flags
  884. 0, 30, // Keepalive
  885. 0, 4, // Client ID length
  886. 116, 101, 115, 116, // Client ID
  887. 0, 8, // Username length
  888. 117, 115, 101, 114, 110, 97, 109, 101, // Username payload
  889. 0, 0 // Password length
  890. // Empty password payload
  891. ]))
  892. testParseGenerate('empty string password payload', {
  893. cmd: 'connect',
  894. retain: false,
  895. qos: 0,
  896. dup: false,
  897. length: 30,
  898. protocolId: 'MQIsdp',
  899. protocolVersion: 3,
  900. clean: true,
  901. keepalive: 30,
  902. clientId: 'test',
  903. username: 'username',
  904. password: ''
  905. }, Buffer.from([
  906. 16, 30, // Header
  907. 0, 6, // Protocol ID length
  908. 77, 81, 73, 115, 100, 112, // Protocol ID
  909. 3, // Protocol version
  910. 194, // Connect flags
  911. 0, 30, // Keepalive
  912. 0, 4, // Client ID length
  913. 116, 101, 115, 116, // Client ID
  914. 0, 8, // Username length
  915. 117, 115, 101, 114, 110, 97, 109, 101, // Username payload
  916. 0, 0 // Password length
  917. // Empty password payload
  918. ]))
  919. testParseGenerate('empty string username and password payload', {
  920. cmd: 'connect',
  921. retain: false,
  922. qos: 0,
  923. dup: false,
  924. length: 22,
  925. protocolId: 'MQIsdp',
  926. protocolVersion: 3,
  927. clean: true,
  928. keepalive: 30,
  929. clientId: 'test',
  930. username: '',
  931. password: Buffer.from('')
  932. }, Buffer.from([
  933. 16, 22, // Header
  934. 0, 6, // Protocol ID length
  935. 77, 81, 73, 115, 100, 112, // Protocol ID
  936. 3, // Protocol version
  937. 194, // Connect flags
  938. 0, 30, // Keepalive
  939. 0, 4, // Client ID length
  940. 116, 101, 115, 116, // Client ID
  941. 0, 0, // Username length
  942. // Empty Username payload
  943. 0, 0 // Password length
  944. // Empty password payload
  945. ]))
  946. testParseGenerate('maximal connect', {
  947. cmd: 'connect',
  948. retain: false,
  949. qos: 0,
  950. dup: false,
  951. length: 54,
  952. protocolId: 'MQIsdp',
  953. protocolVersion: 3,
  954. will: {
  955. retain: true,
  956. qos: 2,
  957. topic: 'topic',
  958. payload: Buffer.from('payload')
  959. },
  960. clean: true,
  961. keepalive: 30,
  962. clientId: 'test',
  963. username: 'username',
  964. password: Buffer.from('password')
  965. }, Buffer.from([
  966. 16, 54, // Header
  967. 0, 6, // Protocol ID length
  968. 77, 81, 73, 115, 100, 112, // Protocol ID
  969. 3, // Protocol version
  970. 246, // Connect flags
  971. 0, 30, // Keepalive
  972. 0, 4, // Client ID length
  973. 116, 101, 115, 116, // Client ID
  974. 0, 5, // Will topic length
  975. 116, 111, 112, 105, 99, // Will topic
  976. 0, 7, // Will payload length
  977. 112, 97, 121, 108, 111, 97, 100, // Will payload
  978. 0, 8, // Username length
  979. 117, 115, 101, 114, 110, 97, 109, 101, // Username
  980. 0, 8, // Password length
  981. 112, 97, 115, 115, 119, 111, 114, 100 // Password
  982. ]))
  983. testParseGenerate('max connect with special chars', {
  984. cmd: 'connect',
  985. retain: false,
  986. qos: 0,
  987. dup: false,
  988. length: 57,
  989. protocolId: 'MQIsdp',
  990. protocolVersion: 3,
  991. will: {
  992. retain: true,
  993. qos: 2,
  994. topic: 'tòpic',
  995. payload: Buffer.from('pay£oad')
  996. },
  997. clean: true,
  998. keepalive: 30,
  999. clientId: 'te$t',
  1000. username: 'u$ern4me',
  1001. password: Buffer.from('p4$$w0£d')
  1002. }, Buffer.from([
  1003. 16, 57, // Header
  1004. 0, 6, // Protocol ID length
  1005. 77, 81, 73, 115, 100, 112, // Protocol ID
  1006. 3, // Protocol version
  1007. 246, // Connect flags
  1008. 0, 30, // Keepalive
  1009. 0, 4, // Client ID length
  1010. 116, 101, 36, 116, // Client ID
  1011. 0, 6, // Will topic length
  1012. 116, 195, 178, 112, 105, 99, // Will topic
  1013. 0, 8, // Will payload length
  1014. 112, 97, 121, 194, 163, 111, 97, 100, // Will payload
  1015. 0, 8, // Username length
  1016. 117, 36, 101, 114, 110, 52, 109, 101, // Username
  1017. 0, 9, // Password length
  1018. 112, 52, 36, 36, 119, 48, 194, 163, 100 // Password
  1019. ]))
  1020. testGenerateOnly('connect all strings generate', {
  1021. cmd: 'connect',
  1022. retain: false,
  1023. qos: 0,
  1024. dup: false,
  1025. length: 54,
  1026. protocolId: 'MQIsdp',
  1027. protocolVersion: 3,
  1028. will: {
  1029. retain: true,
  1030. qos: 2,
  1031. topic: 'topic',
  1032. payload: 'payload'
  1033. },
  1034. clean: true,
  1035. keepalive: 30,
  1036. clientId: 'test',
  1037. username: 'username',
  1038. password: 'password'
  1039. }, Buffer.from([
  1040. 16, 54, // Header
  1041. 0, 6, // Protocol ID length
  1042. 77, 81, 73, 115, 100, 112, // Protocol ID
  1043. 3, // Protocol version
  1044. 246, // Connect flags
  1045. 0, 30, // Keepalive
  1046. 0, 4, // Client ID length
  1047. 116, 101, 115, 116, // Client ID
  1048. 0, 5, // Will topic length
  1049. 116, 111, 112, 105, 99, // Will topic
  1050. 0, 7, // Will payload length
  1051. 112, 97, 121, 108, 111, 97, 100, // Will payload
  1052. 0, 8, // Username length
  1053. 117, 115, 101, 114, 110, 97, 109, 101, // Username
  1054. 0, 8, // Password length
  1055. 112, 97, 115, 115, 119, 111, 114, 100 // Password
  1056. ]))
  1057. testParseError('Cannot parse protocolId', Buffer.from([
  1058. 16, 4,
  1059. 0, 6,
  1060. 77, 81
  1061. ]))
  1062. // missing protocol version on connect
  1063. testParseError('Packet too short', Buffer.from([
  1064. 16, 8, // Header
  1065. 0, 6, // Protocol ID length
  1066. 77, 81, 73, 115, 100, 112 // Protocol ID
  1067. ]))
  1068. // missing keepalive on connect
  1069. testParseError('Packet too short', Buffer.from([
  1070. 16, 10, // Header
  1071. 0, 6, // Protocol ID length
  1072. 77, 81, 73, 115, 100, 112, // Protocol ID
  1073. 3, // Protocol version
  1074. 246 // Connect flags
  1075. ]))
  1076. // missing clientid on connect
  1077. testParseError('Packet too short', Buffer.from([
  1078. 16, 10, // Header
  1079. 0, 6, // Protocol ID length
  1080. 77, 81, 73, 115, 100, 112, // Protocol ID
  1081. 3, // Protocol version
  1082. 246, // Connect flags
  1083. 0, 30 // Keepalive
  1084. ]))
  1085. // missing will topic on connect
  1086. testParseError('Cannot parse will topic', Buffer.from([
  1087. 16, 16, // Header
  1088. 0, 6, // Protocol ID length
  1089. 77, 81, 73, 115, 100, 112, // Protocol ID
  1090. 3, // Protocol version
  1091. 246, // Connect flags
  1092. 0, 30, // Keepalive
  1093. 0, 2, // Will topic length
  1094. 0, 0 // Will topic
  1095. ]))
  1096. // missing will payload on connect
  1097. testParseError('Cannot parse will payload', Buffer.from([
  1098. 16, 23, // Header
  1099. 0, 6, // Protocol ID length
  1100. 77, 81, 73, 115, 100, 112, // Protocol ID
  1101. 3, // Protocol version
  1102. 246, // Connect flags
  1103. 0, 30, // Keepalive
  1104. 0, 5, // Will topic length
  1105. 116, 111, 112, 105, 99, // Will topic
  1106. 0, 2, // Will payload length
  1107. 0, 0 // Will payload
  1108. ]))
  1109. // missing username on connect
  1110. testParseError('Cannot parse username', Buffer.from([
  1111. 16, 32, // Header
  1112. 0, 6, // Protocol ID length
  1113. 77, 81, 73, 115, 100, 112, // Protocol ID
  1114. 3, // Protocol version
  1115. 246, // Connect flags
  1116. 0, 30, // Keepalive
  1117. 0, 5, // Will topic length
  1118. 116, 111, 112, 105, 99, // Will topic
  1119. 0, 7, // Will payload length
  1120. 112, 97, 121, 108, 111, 97, 100, // Will payload
  1121. 0, 2, // Username length
  1122. 0, 0 // Username
  1123. ]))
  1124. // missing password on connect
  1125. testParseError('Cannot parse password', Buffer.from([
  1126. 16, 42, // Header
  1127. 0, 6, // Protocol ID length
  1128. 77, 81, 73, 115, 100, 112, // Protocol ID
  1129. 3, // Protocol version
  1130. 246, // Connect flags
  1131. 0, 30, // Keepalive
  1132. 0, 5, // Will topic length
  1133. 116, 111, 112, 105, 99, // Will topic
  1134. 0, 7, // Will payload length
  1135. 112, 97, 121, 108, 111, 97, 100, // Will payload
  1136. 0, 8, // Username length
  1137. 117, 115, 101, 114, 110, 97, 109, 101, // Username
  1138. 0, 2, // Password length
  1139. 0, 0 // Password
  1140. ]))
  1141. testParseGenerate('connack with return code 0', {
  1142. cmd: 'connack',
  1143. retain: false,
  1144. qos: 0,
  1145. dup: false,
  1146. length: 2,
  1147. sessionPresent: false,
  1148. returnCode: 0
  1149. }, Buffer.from([
  1150. 32, 2, 0, 0
  1151. ]))
  1152. testParseGenerate('connack MQTT 5 with properties', {
  1153. cmd: 'connack',
  1154. retain: false,
  1155. qos: 0,
  1156. dup: false,
  1157. length: 87,
  1158. sessionPresent: false,
  1159. reasonCode: 0,
  1160. properties: {
  1161. sessionExpiryInterval: 1234,
  1162. receiveMaximum: 432,
  1163. maximumQoS: 2,
  1164. retainAvailable: true,
  1165. maximumPacketSize: 100,
  1166. assignedClientIdentifier: 'test',
  1167. topicAliasMaximum: 456,
  1168. reasonString: 'test',
  1169. userProperties: {
  1170. test: 'test'
  1171. },
  1172. wildcardSubscriptionAvailable: true,
  1173. subscriptionIdentifiersAvailable: true,
  1174. sharedSubscriptionAvailable: false,
  1175. serverKeepAlive: 1234,
  1176. responseInformation: 'test',
  1177. serverReference: 'test',
  1178. authenticationMethod: 'test',
  1179. authenticationData: Buffer.from([1, 2, 3, 4])
  1180. }
  1181. }, Buffer.from([
  1182. 32, 87, 0, 0,
  1183. 84, // properties length
  1184. 17, 0, 0, 4, 210, // sessionExpiryInterval
  1185. 33, 1, 176, // receiveMaximum
  1186. 36, 2, // Maximum qos
  1187. 37, 1, // retainAvailable
  1188. 39, 0, 0, 0, 100, // maximumPacketSize
  1189. 18, 0, 4, 116, 101, 115, 116, // assignedClientIdentifier
  1190. 34, 1, 200, // topicAliasMaximum
  1191. 31, 0, 4, 116, 101, 115, 116, // reasonString
  1192. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1193. 40, 1, // wildcardSubscriptionAvailable
  1194. 41, 1, // subscriptionIdentifiersAvailable
  1195. 42, 0, // sharedSubscriptionAvailable
  1196. 19, 4, 210, // serverKeepAlive
  1197. 26, 0, 4, 116, 101, 115, 116, // responseInformation
  1198. 28, 0, 4, 116, 101, 115, 116, // serverReference
  1199. 21, 0, 4, 116, 101, 115, 116, // authenticationMethod
  1200. 22, 0, 4, 1, 2, 3, 4 // authenticationData
  1201. ]), { protocolVersion: 5 })
  1202. testParseGenerate('connack MQTT 5 with properties and doubled user properties', {
  1203. cmd: 'connack',
  1204. retain: false,
  1205. qos: 0,
  1206. dup: false,
  1207. length: 100,
  1208. sessionPresent: false,
  1209. reasonCode: 0,
  1210. properties: {
  1211. sessionExpiryInterval: 1234,
  1212. receiveMaximum: 432,
  1213. maximumQoS: 2,
  1214. retainAvailable: true,
  1215. maximumPacketSize: 100,
  1216. assignedClientIdentifier: 'test',
  1217. topicAliasMaximum: 456,
  1218. reasonString: 'test',
  1219. userProperties: {
  1220. test: ['test', 'test']
  1221. },
  1222. wildcardSubscriptionAvailable: true,
  1223. subscriptionIdentifiersAvailable: true,
  1224. sharedSubscriptionAvailable: false,
  1225. serverKeepAlive: 1234,
  1226. responseInformation: 'test',
  1227. serverReference: 'test',
  1228. authenticationMethod: 'test',
  1229. authenticationData: Buffer.from([1, 2, 3, 4])
  1230. }
  1231. }, Buffer.from([
  1232. 32, 100, 0, 0,
  1233. 97, // properties length
  1234. 17, 0, 0, 4, 210, // sessionExpiryInterval
  1235. 33, 1, 176, // receiveMaximum
  1236. 36, 2, // Maximum qos
  1237. 37, 1, // retainAvailable
  1238. 39, 0, 0, 0, 100, // maximumPacketSize
  1239. 18, 0, 4, 116, 101, 115, 116, // assignedClientIdentifier
  1240. 34, 1, 200, // topicAliasMaximum
  1241. 31, 0, 4, 116, 101, 115, 116, // reasonString
  1242. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116,
  1243. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1244. 40, 1, // wildcardSubscriptionAvailable
  1245. 41, 1, // subscriptionIdentifiersAvailable
  1246. 42, 0, // sharedSubscriptionAvailable
  1247. 19, 4, 210, // serverKeepAlive
  1248. 26, 0, 4, 116, 101, 115, 116, // responseInformation
  1249. 28, 0, 4, 116, 101, 115, 116, // serverReference
  1250. 21, 0, 4, 116, 101, 115, 116, // authenticationMethod
  1251. 22, 0, 4, 1, 2, 3, 4 // authenticationData
  1252. ]), { protocolVersion: 5 })
  1253. testParseGenerate('connack with return code 0 session present bit set', {
  1254. cmd: 'connack',
  1255. retain: false,
  1256. qos: 0,
  1257. dup: false,
  1258. length: 2,
  1259. sessionPresent: true,
  1260. returnCode: 0
  1261. }, Buffer.from([
  1262. 32, 2, 1, 0
  1263. ]))
  1264. testParseGenerate('connack with return code 5', {
  1265. cmd: 'connack',
  1266. retain: false,
  1267. qos: 0,
  1268. dup: false,
  1269. length: 2,
  1270. sessionPresent: false,
  1271. returnCode: 5
  1272. }, Buffer.from([
  1273. 32, 2, 0, 5
  1274. ]))
  1275. testGenerateError('Invalid return code', {
  1276. cmd: 'connack',
  1277. retain: false,
  1278. qos: 0,
  1279. dup: false,
  1280. length: 2,
  1281. sessionPresent: false,
  1282. returnCode: '5' // returncode must be a number
  1283. })
  1284. testParseGenerate('minimal publish', {
  1285. cmd: 'publish',
  1286. retain: false,
  1287. qos: 0,
  1288. dup: false,
  1289. length: 10,
  1290. topic: 'test',
  1291. payload: Buffer.from('test')
  1292. }, Buffer.from([
  1293. 48, 10, // Header
  1294. 0, 4, // Topic length
  1295. 116, 101, 115, 116, // Topic (test)
  1296. 116, 101, 115, 116 // Payload (test)
  1297. ]))
  1298. testParseGenerate('publish MQTT 5 properties', {
  1299. cmd: 'publish',
  1300. retain: true,
  1301. qos: 2,
  1302. dup: true,
  1303. length: 86,
  1304. topic: 'test',
  1305. payload: Buffer.from('test'),
  1306. messageId: 10,
  1307. properties: {
  1308. payloadFormatIndicator: true,
  1309. messageExpiryInterval: 4321,
  1310. topicAlias: 100,
  1311. responseTopic: 'topic',
  1312. correlationData: Buffer.from([1, 2, 3, 4]),
  1313. userProperties: {
  1314. test: ['test', 'test', 'test']
  1315. },
  1316. subscriptionIdentifier: 120,
  1317. contentType: 'test'
  1318. }
  1319. }, Buffer.from([
  1320. 61, 86, // Header
  1321. 0, 4, // Topic length
  1322. 116, 101, 115, 116, // Topic (test)
  1323. 0, 10, // Message ID
  1324. 73, // properties length
  1325. 1, 1, // payloadFormatIndicator
  1326. 2, 0, 0, 16, 225, // message expiry interval
  1327. 35, 0, 100, // topicAlias
  1328. 8, 0, 5, 116, 111, 112, 105, 99, // response topic
  1329. 9, 0, 4, 1, 2, 3, 4, // correlationData
  1330. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1331. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1332. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1333. 11, 120, // subscriptionIdentifier
  1334. 3, 0, 4, 116, 101, 115, 116, // content type
  1335. 116, 101, 115, 116 // Payload (test)
  1336. ]), { protocolVersion: 5 })
  1337. testParseGenerate('publish MQTT 5 with multiple same properties', {
  1338. cmd: 'publish',
  1339. retain: true,
  1340. qos: 2,
  1341. dup: true,
  1342. length: 64,
  1343. topic: 'test',
  1344. payload: Buffer.from('test'),
  1345. messageId: 10,
  1346. properties: {
  1347. payloadFormatIndicator: true,
  1348. messageExpiryInterval: 4321,
  1349. topicAlias: 100,
  1350. responseTopic: 'topic',
  1351. correlationData: Buffer.from([1, 2, 3, 4]),
  1352. userProperties: {
  1353. test: 'test'
  1354. },
  1355. subscriptionIdentifier: [120, 121, 122],
  1356. contentType: 'test'
  1357. }
  1358. }, Buffer.from([
  1359. 61, 64, // Header
  1360. 0, 4, // Topic length
  1361. 116, 101, 115, 116, // Topic (test)
  1362. 0, 10, // Message ID
  1363. 51, // properties length
  1364. 1, 1, // payloadFormatIndicator
  1365. 2, 0, 0, 16, 225, // message expiry interval
  1366. 35, 0, 100, // topicAlias
  1367. 8, 0, 5, 116, 111, 112, 105, 99, // response topic
  1368. 9, 0, 4, 1, 2, 3, 4, // correlationData
  1369. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1370. 11, 120, // subscriptionIdentifier
  1371. 11, 121, // subscriptionIdentifier
  1372. 11, 122, // subscriptionIdentifier
  1373. 3, 0, 4, 116, 101, 115, 116, // content type
  1374. 116, 101, 115, 116 // Payload (test)
  1375. ]), { protocolVersion: 5 })
  1376. testParseGenerate('publish MQTT 5 properties with 0-4 byte varbyte', {
  1377. cmd: 'publish',
  1378. retain: true,
  1379. qos: 2,
  1380. dup: true,
  1381. length: 27,
  1382. topic: 'test',
  1383. payload: Buffer.from('test'),
  1384. messageId: 10,
  1385. properties: {
  1386. payloadFormatIndicator: false,
  1387. subscriptionIdentifier: [128, 16384, 2097152] // this tests the varbyte handling
  1388. }
  1389. }, Buffer.from([
  1390. 61, 27, // Header
  1391. 0, 4, // Topic length
  1392. 116, 101, 115, 116, // Topic (test)
  1393. 0, 10, // Message ID
  1394. 14, // properties length
  1395. 1, 0, // payloadFormatIndicator
  1396. 11, 128, 1, // subscriptionIdentifier
  1397. 11, 128, 128, 1, // subscriptionIdentifier
  1398. 11, 128, 128, 128, 1, // subscriptionIdentifier
  1399. 116, 101, 115, 116 // Payload (test)
  1400. ]), { protocolVersion: 5 })
  1401. testParseGenerate('publish MQTT 5 properties with max value varbyte', {
  1402. cmd: 'publish',
  1403. retain: true,
  1404. qos: 2,
  1405. dup: true,
  1406. length: 22,
  1407. topic: 'test',
  1408. payload: Buffer.from('test'),
  1409. messageId: 10,
  1410. properties: {
  1411. payloadFormatIndicator: false,
  1412. subscriptionIdentifier: [1, 268435455]
  1413. }
  1414. }, Buffer.from([
  1415. 61, 22, // Header
  1416. 0, 4, // Topic length
  1417. 116, 101, 115, 116, // Topic (test)
  1418. 0, 10, // Message ID
  1419. 9, // properties length
  1420. 1, 0, // payloadFormatIndicator
  1421. 11, 1, // subscriptionIdentifier
  1422. 11, 255, 255, 255, 127, // subscriptionIdentifier (max value)
  1423. 116, 101, 115, 116 // Payload (test)
  1424. ]), { protocolVersion: 5 })
  1425. ; (() => {
  1426. const buffer = Buffer.alloc(2048)
  1427. testParseGenerate('2KB publish packet', {
  1428. cmd: 'publish',
  1429. retain: false,
  1430. qos: 0,
  1431. dup: false,
  1432. length: 2054,
  1433. topic: 'test',
  1434. payload: buffer
  1435. }, Buffer.concat([Buffer.from([
  1436. 48, 134, 16, // Header
  1437. 0, 4, // Topic length
  1438. 116, 101, 115, 116 // Topic (test)
  1439. ]), buffer]))
  1440. })()
  1441. ; (() => {
  1442. const maxLength = 268435455
  1443. const buffer = Buffer.alloc(maxLength - 6)
  1444. testParseGenerate('Max payload publish packet', {
  1445. cmd: 'publish',
  1446. retain: false,
  1447. qos: 0,
  1448. dup: false,
  1449. length: maxLength,
  1450. topic: 'test',
  1451. payload: buffer
  1452. }, Buffer.concat([Buffer.from([
  1453. 48, 255, 255, 255, 127, // Header
  1454. 0, 4, // Topic length
  1455. 116, 101, 115, 116 // Topic (test)
  1456. ]), buffer]))
  1457. })()
  1458. testParseGenerate('maximal publish', {
  1459. cmd: 'publish',
  1460. retain: true,
  1461. qos: 2,
  1462. length: 12,
  1463. dup: true,
  1464. topic: 'test',
  1465. messageId: 10,
  1466. payload: Buffer.from('test')
  1467. }, Buffer.from([
  1468. 61, 12, // Header
  1469. 0, 4, // Topic length
  1470. 116, 101, 115, 116, // Topic
  1471. 0, 10, // Message ID
  1472. 116, 101, 115, 116 // Payload
  1473. ]))
  1474. test('publish all strings generate', t => {
  1475. const message = {
  1476. cmd: 'publish',
  1477. retain: true,
  1478. qos: 2,
  1479. length: 12,
  1480. dup: true,
  1481. topic: 'test',
  1482. messageId: 10,
  1483. payload: Buffer.from('test')
  1484. }
  1485. const expected = Buffer.from([
  1486. 61, 12, // Header
  1487. 0, 4, // Topic length
  1488. 116, 101, 115, 116, // Topic
  1489. 0, 10, // Message ID
  1490. 116, 101, 115, 116 // Payload
  1491. ])
  1492. t.equal(mqtt.generate(message).toString('hex'), expected.toString('hex'))
  1493. t.end()
  1494. })
  1495. testParseGenerate('empty publish', {
  1496. cmd: 'publish',
  1497. retain: false,
  1498. qos: 0,
  1499. dup: false,
  1500. length: 6,
  1501. topic: 'test',
  1502. payload: Buffer.alloc(0)
  1503. }, Buffer.from([
  1504. 48, 6, // Header
  1505. 0, 4, // Topic length
  1506. 116, 101, 115, 116 // Topic
  1507. // Empty payload
  1508. ]))
  1509. test('splitted publish parse', t => {
  1510. t.plan(3)
  1511. const parser = mqtt.parser()
  1512. const expected = {
  1513. cmd: 'publish',
  1514. retain: false,
  1515. qos: 0,
  1516. dup: false,
  1517. length: 10,
  1518. topic: 'test',
  1519. payload: Buffer.from('test')
  1520. }
  1521. parser.on('packet', packet => {
  1522. t.deepLooseEqual(packet, expected, 'expected packet')
  1523. })
  1524. t.equal(parser.parse(Buffer.from([
  1525. 48, 10, // Header
  1526. 0, 4, // Topic length
  1527. 116, 101, 115, 116 // Topic (test)
  1528. ])), 6, 'remaining bytes')
  1529. t.equal(parser.parse(Buffer.from([
  1530. 116, 101, 115, 116 // Payload (test)
  1531. ])), 0, 'remaining bytes')
  1532. })
  1533. test('split publish longer', t => {
  1534. t.plan(3)
  1535. const length = 255
  1536. const topic = 'test'
  1537. // Minus two bytes for the topic length specifier
  1538. const payloadLength = length - topic.length - 2
  1539. const parser = mqtt.parser()
  1540. const expected = {
  1541. cmd: 'publish',
  1542. retain: false,
  1543. qos: 0,
  1544. dup: false,
  1545. length: length,
  1546. topic: topic,
  1547. payload: Buffer.from('a'.repeat(payloadLength))
  1548. }
  1549. parser.on('packet', packet => {
  1550. t.deepLooseEqual(packet, expected, 'expected packet')
  1551. })
  1552. t.equal(parser.parse(Buffer.from([
  1553. 48, 255, 1, // Header
  1554. 0, topic.length, // Topic length
  1555. 116, 101, 115, 116 // Topic (test)
  1556. ])), 6, 'remaining bytes')
  1557. t.equal(parser.parse(Buffer.from(Array(payloadLength).fill(97))),
  1558. 0, 'remaining bytes')
  1559. })
  1560. test('split length parse', t => {
  1561. t.plan(4)
  1562. const length = 255
  1563. const topic = 'test'
  1564. const payloadLength = length - topic.length - 2
  1565. const parser = mqtt.parser()
  1566. const expected = {
  1567. cmd: 'publish',
  1568. retain: false,
  1569. qos: 0,
  1570. dup: false,
  1571. length: length,
  1572. topic: topic,
  1573. payload: Buffer.from('a'.repeat(payloadLength))
  1574. }
  1575. parser.on('packet', packet => {
  1576. t.deepLooseEqual(packet, expected, 'expected packet')
  1577. })
  1578. t.equal(parser.parse(Buffer.from([
  1579. 48, 255 // Header (partial length)
  1580. ])), 1, 'remaining bytes')
  1581. t.equal(parser.parse(Buffer.from([
  1582. 1, // Rest of header length
  1583. 0, topic.length, // Topic length
  1584. 116, 101, 115, 116 // Topic (test)
  1585. ])), 6, 'remaining bytes')
  1586. t.equal(parser.parse(Buffer.from(Array(payloadLength).fill(97))),
  1587. 0, 'remaining bytes')
  1588. })
  1589. testGenerateError('Invalid variable byte integer: 268435456', {
  1590. cmd: 'publish',
  1591. retain: false,
  1592. qos: 0,
  1593. dup: false,
  1594. length: (268435455 + 1),
  1595. topic: 'test',
  1596. payload: Buffer.alloc(268435455 + 1 - 6)
  1597. }, {}, 'Length var byte integer over max allowed value throws error')
  1598. testGenerateError('Invalid subscriptionIdentifier: 268435456', {
  1599. cmd: 'publish',
  1600. retain: true,
  1601. qos: 2,
  1602. dup: true,
  1603. length: 27,
  1604. topic: 'test',
  1605. payload: Buffer.from('test'),
  1606. messageId: 10,
  1607. properties: {
  1608. payloadFormatIndicator: false,
  1609. subscriptionIdentifier: 268435456
  1610. }
  1611. }, { protocolVersion: 5 }, 'MQTT 5.0 var byte integer >24 bits throws error')
  1612. testParseGenerate('puback', {
  1613. cmd: 'puback',
  1614. retain: false,
  1615. qos: 0,
  1616. dup: false,
  1617. length: 2,
  1618. messageId: 2
  1619. }, Buffer.from([
  1620. 64, 2, // Header
  1621. 0, 2 // Message ID
  1622. ]))
  1623. testParseGenerate('puback with reason and no MQTT 5 properties', {
  1624. cmd: 'puback',
  1625. retain: false,
  1626. qos: 0,
  1627. dup: false,
  1628. length: 3,
  1629. messageId: 2,
  1630. reasonCode: 16
  1631. }, Buffer.from([
  1632. 64, 3, // Header
  1633. 0, 2, // Message ID
  1634. 16 // reason code
  1635. ]), { protocolVersion: 5 })
  1636. testParseGenerate('puback MQTT 5 properties', {
  1637. cmd: 'puback',
  1638. retain: false,
  1639. qos: 0,
  1640. dup: false,
  1641. length: 24,
  1642. messageId: 2,
  1643. reasonCode: 16,
  1644. properties: {
  1645. reasonString: 'test',
  1646. userProperties: {
  1647. test: 'test'
  1648. }
  1649. }
  1650. }, Buffer.from([
  1651. 64, 24, // Header
  1652. 0, 2, // Message ID
  1653. 16, // reason code
  1654. 20, // properties length
  1655. 31, 0, 4, 116, 101, 115, 116, // reasonString
  1656. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116 // userProperties
  1657. ]), { protocolVersion: 5 })
  1658. testParseGenerate('pubrec', {
  1659. cmd: 'pubrec',
  1660. retain: false,
  1661. qos: 0,
  1662. dup: false,
  1663. length: 2,
  1664. messageId: 2
  1665. }, Buffer.from([
  1666. 80, 2, // Header
  1667. 0, 2 // Message ID
  1668. ]))
  1669. testParseGenerate('pubrec MQTT 5 properties', {
  1670. cmd: 'pubrec',
  1671. retain: false,
  1672. qos: 0,
  1673. dup: false,
  1674. length: 24,
  1675. messageId: 2,
  1676. reasonCode: 16,
  1677. properties: {
  1678. reasonString: 'test',
  1679. userProperties: {
  1680. test: 'test'
  1681. }
  1682. }
  1683. }, Buffer.from([
  1684. 80, 24, // Header
  1685. 0, 2, // Message ID
  1686. 16, // reason code
  1687. 20, // properties length
  1688. 31, 0, 4, 116, 101, 115, 116, // reasonString
  1689. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116 // userProperties
  1690. ]), { protocolVersion: 5 })
  1691. testParseGenerate('pubrel', {
  1692. cmd: 'pubrel',
  1693. retain: false,
  1694. qos: 1,
  1695. dup: false,
  1696. length: 2,
  1697. messageId: 2
  1698. }, Buffer.from([
  1699. 98, 2, // Header
  1700. 0, 2 // Message ID
  1701. ]))
  1702. testParseGenerate('pubrel MQTT5 properties', {
  1703. cmd: 'pubrel',
  1704. retain: false,
  1705. qos: 1,
  1706. dup: false,
  1707. length: 24,
  1708. messageId: 2,
  1709. reasonCode: 16,
  1710. properties: {
  1711. reasonString: 'test',
  1712. userProperties: {
  1713. test: 'test'
  1714. }
  1715. }
  1716. }, Buffer.from([
  1717. 98, 24, // Header
  1718. 0, 2, // Message ID
  1719. 16, // reason code
  1720. 20, // properties length
  1721. 31, 0, 4, 116, 101, 115, 116, // reasonString
  1722. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116 // userProperties
  1723. ]), { protocolVersion: 5 })
  1724. testParseGenerate('pubcomp', {
  1725. cmd: 'pubcomp',
  1726. retain: false,
  1727. qos: 0,
  1728. dup: false,
  1729. length: 2,
  1730. messageId: 2
  1731. }, Buffer.from([
  1732. 112, 2, // Header
  1733. 0, 2 // Message ID
  1734. ]))
  1735. testParseGenerate('pubcomp MQTT 5 properties', {
  1736. cmd: 'pubcomp',
  1737. retain: false,
  1738. qos: 0,
  1739. dup: false,
  1740. length: 24,
  1741. messageId: 2,
  1742. reasonCode: 16,
  1743. properties: {
  1744. reasonString: 'test',
  1745. userProperties: {
  1746. test: 'test'
  1747. }
  1748. }
  1749. }, Buffer.from([
  1750. 112, 24, // Header
  1751. 0, 2, // Message ID
  1752. 16, // reason code
  1753. 20, // properties length
  1754. 31, 0, 4, 116, 101, 115, 116, // reasonString
  1755. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116 // userProperties
  1756. ]), { protocolVersion: 5 })
  1757. testParseError('Wrong subscribe header', Buffer.from([
  1758. 128, 9, // Header (subscribeqos=0length=9)
  1759. 0, 6, // Message ID (6)
  1760. 0, 4, // Topic length,
  1761. 116, 101, 115, 116, // Topic (test)
  1762. 0 // Qos (0)
  1763. ]))
  1764. testParseGenerate('subscribe to one topic', {
  1765. cmd: 'subscribe',
  1766. retain: false,
  1767. qos: 1,
  1768. dup: false,
  1769. length: 9,
  1770. subscriptions: [
  1771. {
  1772. topic: 'test',
  1773. qos: 0
  1774. }
  1775. ],
  1776. messageId: 6
  1777. }, Buffer.from([
  1778. 130, 9, // Header (subscribeqos=1length=9)
  1779. 0, 6, // Message ID (6)
  1780. 0, 4, // Topic length,
  1781. 116, 101, 115, 116, // Topic (test)
  1782. 0 // Qos (0)
  1783. ]))
  1784. testParseGenerate('subscribe to one topic by MQTT 5', {
  1785. cmd: 'subscribe',
  1786. retain: false,
  1787. qos: 1,
  1788. dup: false,
  1789. length: 26,
  1790. subscriptions: [
  1791. {
  1792. topic: 'test',
  1793. qos: 0,
  1794. nl: false,
  1795. rap: true,
  1796. rh: 1
  1797. }
  1798. ],
  1799. messageId: 6,
  1800. properties: {
  1801. subscriptionIdentifier: 145,
  1802. userProperties: {
  1803. test: 'test'
  1804. }
  1805. }
  1806. }, Buffer.from([
  1807. 130, 26, // Header (subscribeqos=1length=9)
  1808. 0, 6, // Message ID (6)
  1809. 16, // properties length
  1810. 11, 145, 1, // subscriptionIdentifier
  1811. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1812. 0, 4, // Topic length,
  1813. 116, 101, 115, 116, // Topic (test)
  1814. 24 // settings(qos: 0, noLocal: false, Retain as Published: true, retain handling: 1)
  1815. ]), { protocolVersion: 5 })
  1816. testParseGenerate('subscribe to three topics', {
  1817. cmd: 'subscribe',
  1818. retain: false,
  1819. qos: 1,
  1820. dup: false,
  1821. length: 23,
  1822. subscriptions: [
  1823. {
  1824. topic: 'test',
  1825. qos: 0
  1826. }, {
  1827. topic: 'uest',
  1828. qos: 1
  1829. }, {
  1830. topic: 'tfst',
  1831. qos: 2
  1832. }
  1833. ],
  1834. messageId: 6
  1835. }, Buffer.from([
  1836. 130, 23, // Header (publishqos=1length=9)
  1837. 0, 6, // Message ID (6)
  1838. 0, 4, // Topic length,
  1839. 116, 101, 115, 116, // Topic (test)
  1840. 0, // Qos (0)
  1841. 0, 4, // Topic length
  1842. 117, 101, 115, 116, // Topic (uest)
  1843. 1, // Qos (1)
  1844. 0, 4, // Topic length
  1845. 116, 102, 115, 116, // Topic (tfst)
  1846. 2 // Qos (2)
  1847. ]))
  1848. testParseGenerate('subscribe to 3 topics by MQTT 5', {
  1849. cmd: 'subscribe',
  1850. retain: false,
  1851. qos: 1,
  1852. dup: false,
  1853. length: 40,
  1854. subscriptions: [
  1855. {
  1856. topic: 'test',
  1857. qos: 0,
  1858. nl: false,
  1859. rap: true,
  1860. rh: 1
  1861. },
  1862. {
  1863. topic: 'uest',
  1864. qos: 1,
  1865. nl: false,
  1866. rap: false,
  1867. rh: 0
  1868. }, {
  1869. topic: 'tfst',
  1870. qos: 2,
  1871. nl: true,
  1872. rap: false,
  1873. rh: 0
  1874. }
  1875. ],
  1876. messageId: 6,
  1877. properties: {
  1878. subscriptionIdentifier: 145,
  1879. userProperties: {
  1880. test: 'test'
  1881. }
  1882. }
  1883. }, Buffer.from([
  1884. 130, 40, // Header (subscribeqos=1length=9)
  1885. 0, 6, // Message ID (6)
  1886. 16, // properties length
  1887. 11, 145, 1, // subscriptionIdentifier
  1888. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1889. 0, 4, // Topic length,
  1890. 116, 101, 115, 116, // Topic (test)
  1891. 24, // settings(qos: 0, noLocal: false, Retain as Published: true, retain handling: 1)
  1892. 0, 4, // Topic length
  1893. 117, 101, 115, 116, // Topic (uest)
  1894. 1, // Qos (1)
  1895. 0, 4, // Topic length
  1896. 116, 102, 115, 116, // Topic (tfst)
  1897. 6 // Qos (2), No Local: true
  1898. ]), { protocolVersion: 5 })
  1899. testParseGenerate('suback', {
  1900. cmd: 'suback',
  1901. retain: false,
  1902. qos: 0,
  1903. dup: false,
  1904. length: 6,
  1905. granted: [0, 1, 2, 128],
  1906. messageId: 6
  1907. }, Buffer.from([
  1908. 144, 6, // Header
  1909. 0, 6, // Message ID
  1910. 0, 1, 2, 128 // Granted qos (0, 1, 2) and a rejected being 0x80
  1911. ]))
  1912. testParseGenerate('suback MQTT 5', {
  1913. cmd: 'suback',
  1914. retain: false,
  1915. qos: 0,
  1916. dup: false,
  1917. length: 27,
  1918. granted: [0, 1, 2, 128],
  1919. messageId: 6,
  1920. properties: {
  1921. reasonString: 'test',
  1922. userProperties: {
  1923. test: 'test'
  1924. }
  1925. }
  1926. }, Buffer.from([
  1927. 144, 27, // Header
  1928. 0, 6, // Message ID
  1929. 20, // properties length
  1930. 31, 0, 4, 116, 101, 115, 116, // reasonString
  1931. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1932. 0, 1, 2, 128 // Granted qos (0, 1, 2) and a rejected being 0x80
  1933. ]), { protocolVersion: 5 })
  1934. testParseGenerate('unsubscribe', {
  1935. cmd: 'unsubscribe',
  1936. retain: false,
  1937. qos: 1,
  1938. dup: false,
  1939. length: 14,
  1940. unsubscriptions: [
  1941. 'tfst',
  1942. 'test'
  1943. ],
  1944. messageId: 7
  1945. }, Buffer.from([
  1946. 162, 14,
  1947. 0, 7, // Message ID (7)
  1948. 0, 4, // Topic length
  1949. 116, 102, 115, 116, // Topic (tfst)
  1950. 0, 4, // Topic length,
  1951. 116, 101, 115, 116 // Topic (test)
  1952. ]))
  1953. testGenerateError('Invalid unsubscriptions', {
  1954. cmd: 'unsubscribe',
  1955. retain: false,
  1956. qos: 1,
  1957. dup: true,
  1958. length: 5,
  1959. unsubscriptions: 5,
  1960. messageId: 7
  1961. }, {}, 'unsubscribe with unsubscriptions not an array')
  1962. testGenerateError('Invalid unsubscriptions', {
  1963. cmd: 'unsubscribe',
  1964. retain: false,
  1965. qos: 1,
  1966. dup: true,
  1967. length: 5,
  1968. unsubscriptions: [1, 2],
  1969. messageId: 7
  1970. }, {}, 'unsubscribe with unsubscriptions as an object')
  1971. testParseGenerate('unsubscribe MQTT 5', {
  1972. cmd: 'unsubscribe',
  1973. retain: false,
  1974. qos: 1,
  1975. dup: false,
  1976. length: 28,
  1977. unsubscriptions: [
  1978. 'tfst',
  1979. 'test'
  1980. ],
  1981. messageId: 7,
  1982. properties: {
  1983. userProperties: {
  1984. test: 'test'
  1985. }
  1986. }
  1987. }, Buffer.from([
  1988. 162, 28,
  1989. 0, 7, // Message ID (7)
  1990. 13, // properties length
  1991. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  1992. 0, 4, // Topic length
  1993. 116, 102, 115, 116, // Topic (tfst)
  1994. 0, 4, // Topic length,
  1995. 116, 101, 115, 116 // Topic (test)
  1996. ]), { protocolVersion: 5 })
  1997. testParseGenerate('unsuback', {
  1998. cmd: 'unsuback',
  1999. retain: false,
  2000. qos: 0,
  2001. dup: false,
  2002. length: 2,
  2003. messageId: 8
  2004. }, Buffer.from([
  2005. 176, 2, // Header
  2006. 0, 8 // Message ID
  2007. ]))
  2008. testParseGenerate('unsuback MQTT 5', {
  2009. cmd: 'unsuback',
  2010. retain: false,
  2011. qos: 0,
  2012. dup: false,
  2013. length: 25,
  2014. messageId: 8,
  2015. properties: {
  2016. reasonString: 'test',
  2017. userProperties: {
  2018. test: 'test'
  2019. }
  2020. },
  2021. granted: [0, 128]
  2022. }, Buffer.from([
  2023. 176, 25, // Header
  2024. 0, 8, // Message ID
  2025. 20, // properties length
  2026. 31, 0, 4, 116, 101, 115, 116, // reasonString
  2027. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  2028. 0, 128 // success and error
  2029. ]), { protocolVersion: 5 })
  2030. testParseGenerate('pingreq', {
  2031. cmd: 'pingreq',
  2032. retain: false,
  2033. qos: 0,
  2034. dup: false,
  2035. length: 0
  2036. }, Buffer.from([
  2037. 192, 0 // Header
  2038. ]))
  2039. testParseGenerate('pingresp', {
  2040. cmd: 'pingresp',
  2041. retain: false,
  2042. qos: 0,
  2043. dup: false,
  2044. length: 0
  2045. }, Buffer.from([
  2046. 208, 0 // Header
  2047. ]))
  2048. testParseGenerate('disconnect', {
  2049. cmd: 'disconnect',
  2050. retain: false,
  2051. qos: 0,
  2052. dup: false,
  2053. length: 0
  2054. }, Buffer.from([
  2055. 224, 0 // Header
  2056. ]))
  2057. testParseGenerate('disconnect MQTT 5', {
  2058. cmd: 'disconnect',
  2059. retain: false,
  2060. qos: 0,
  2061. dup: false,
  2062. length: 34,
  2063. reasonCode: 0,
  2064. properties: {
  2065. sessionExpiryInterval: 145,
  2066. reasonString: 'test',
  2067. userProperties: {
  2068. test: 'test'
  2069. },
  2070. serverReference: 'test'
  2071. }
  2072. }, Buffer.from([
  2073. 224, 34, // Header
  2074. 0, // reason code
  2075. 32, // properties length
  2076. 17, 0, 0, 0, 145, // sessionExpiryInterval
  2077. 31, 0, 4, 116, 101, 115, 116, // reasonString
  2078. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  2079. 28, 0, 4, 116, 101, 115, 116// serverReference
  2080. ]), { protocolVersion: 5 })
  2081. testParseGenerate('disconnect MQTT 5 with no properties', {
  2082. cmd: 'disconnect',
  2083. retain: false,
  2084. qos: 0,
  2085. dup: false,
  2086. length: 2,
  2087. reasonCode: 0
  2088. }, Buffer.from([
  2089. 224, 2, // Fixed Header (DISCONNECT, Remaining Length)
  2090. 0, // Reason Code (Normal Disconnection)
  2091. 0 // Property Length (0 => No Properties)
  2092. ]), { protocolVersion: 5 })
  2093. testParseGenerate('auth MQTT 5', {
  2094. cmd: 'auth',
  2095. retain: false,
  2096. qos: 0,
  2097. dup: false,
  2098. length: 36,
  2099. reasonCode: 0,
  2100. properties: {
  2101. authenticationMethod: 'test',
  2102. authenticationData: Buffer.from([0, 1, 2, 3]),
  2103. reasonString: 'test',
  2104. userProperties: {
  2105. test: 'test'
  2106. }
  2107. }
  2108. }, Buffer.from([
  2109. 240, 36, // Header
  2110. 0, // reason code
  2111. 34, // properties length
  2112. 21, 0, 4, 116, 101, 115, 116, // auth method
  2113. 22, 0, 4, 0, 1, 2, 3, // auth data
  2114. 31, 0, 4, 116, 101, 115, 116, // reasonString
  2115. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116 // userProperties
  2116. ]), { protocolVersion: 5 })
  2117. testGenerateError('Invalid protocolId', {
  2118. cmd: 'connect',
  2119. retain: false,
  2120. qos: 0,
  2121. dup: false,
  2122. length: 54,
  2123. protocolId: 42,
  2124. protocolVersion: 3,
  2125. will: {
  2126. retain: true,
  2127. qos: 2,
  2128. topic: 'topic',
  2129. payload: 'payload'
  2130. },
  2131. clean: true,
  2132. keepalive: 30,
  2133. clientId: 'test',
  2134. username: 'username',
  2135. password: 'password'
  2136. })
  2137. testGenerateError('Invalid protocol version', {
  2138. cmd: 'connect',
  2139. retain: false,
  2140. qos: 0,
  2141. dup: false,
  2142. length: 54,
  2143. protocolId: 'MQIsdp',
  2144. protocolVersion: 1,
  2145. will: {
  2146. retain: true,
  2147. qos: 2,
  2148. topic: 'topic',
  2149. payload: 'payload'
  2150. },
  2151. clean: true,
  2152. keepalive: 30,
  2153. clientId: 'test',
  2154. username: 'username',
  2155. password: 'password'
  2156. })
  2157. testGenerateError('clientId must be supplied before 3.1.1', {
  2158. cmd: 'connect',
  2159. retain: false,
  2160. qos: 0,
  2161. dup: false,
  2162. length: 54,
  2163. protocolId: 'MQIsdp',
  2164. protocolVersion: 3,
  2165. will: {
  2166. retain: true,
  2167. qos: 2,
  2168. topic: 'topic',
  2169. payload: 'payload'
  2170. },
  2171. clean: true,
  2172. keepalive: 30,
  2173. username: 'username',
  2174. password: 'password'
  2175. })
  2176. testGenerateError('clientId must be given if cleanSession set to 0', {
  2177. cmd: 'connect',
  2178. retain: false,
  2179. qos: 0,
  2180. dup: false,
  2181. length: 54,
  2182. protocolId: 'MQTT',
  2183. protocolVersion: 4,
  2184. will: {
  2185. retain: true,
  2186. qos: 2,
  2187. topic: 'topic',
  2188. payload: 'payload'
  2189. },
  2190. clean: false,
  2191. keepalive: 30,
  2192. username: 'username',
  2193. password: 'password'
  2194. })
  2195. testGenerateError('Invalid keepalive', {
  2196. cmd: 'connect',
  2197. retain: false,
  2198. qos: 0,
  2199. dup: false,
  2200. length: 54,
  2201. protocolId: 'MQIsdp',
  2202. protocolVersion: 3,
  2203. will: {
  2204. retain: true,
  2205. qos: 2,
  2206. topic: 'topic',
  2207. payload: 'payload'
  2208. },
  2209. clean: true,
  2210. keepalive: 'hello',
  2211. clientId: 'test',
  2212. username: 'username',
  2213. password: 'password'
  2214. })
  2215. testGenerateError('Invalid keepalive', {
  2216. cmd: 'connect',
  2217. keepalive: 3.1416
  2218. })
  2219. testGenerateError('Invalid will', {
  2220. cmd: 'connect',
  2221. retain: false,
  2222. qos: 0,
  2223. dup: false,
  2224. length: 54,
  2225. protocolId: 'MQIsdp',
  2226. protocolVersion: 3,
  2227. will: 42,
  2228. clean: true,
  2229. keepalive: 30,
  2230. clientId: 'test',
  2231. username: 'username',
  2232. password: 'password'
  2233. })
  2234. testGenerateError('Invalid will topic', {
  2235. cmd: 'connect',
  2236. retain: false,
  2237. qos: 0,
  2238. dup: false,
  2239. length: 54,
  2240. protocolId: 'MQIsdp',
  2241. protocolVersion: 3,
  2242. will: {
  2243. retain: true,
  2244. qos: 2,
  2245. payload: 'payload'
  2246. },
  2247. clean: true,
  2248. keepalive: 30,
  2249. clientId: 'test',
  2250. username: 'username',
  2251. password: 'password'
  2252. })
  2253. testGenerateError('Invalid will payload', {
  2254. cmd: 'connect',
  2255. retain: false,
  2256. qos: 0,
  2257. dup: false,
  2258. length: 54,
  2259. protocolId: 'MQIsdp',
  2260. protocolVersion: 3,
  2261. will: {
  2262. retain: true,
  2263. qos: 2,
  2264. topic: 'topic',
  2265. payload: 42
  2266. },
  2267. clean: true,
  2268. keepalive: 30,
  2269. clientId: 'test',
  2270. username: 'username',
  2271. password: 'password'
  2272. })
  2273. testGenerateError('Invalid username', {
  2274. cmd: 'connect',
  2275. retain: false,
  2276. qos: 0,
  2277. dup: false,
  2278. length: 54,
  2279. protocolId: 'MQIsdp',
  2280. protocolVersion: 3,
  2281. will: {
  2282. retain: true,
  2283. qos: 2,
  2284. topic: 'topic',
  2285. payload: 'payload'
  2286. },
  2287. clean: true,
  2288. keepalive: 30,
  2289. clientId: 'test',
  2290. username: 42,
  2291. password: 'password'
  2292. })
  2293. testGenerateError('Invalid password', {
  2294. cmd: 'connect',
  2295. retain: false,
  2296. qos: 0,
  2297. dup: false,
  2298. length: 54,
  2299. protocolId: 'MQIsdp',
  2300. protocolVersion: 3,
  2301. will: {
  2302. retain: true,
  2303. qos: 2,
  2304. topic: 'topic',
  2305. payload: 'payload'
  2306. },
  2307. clean: true,
  2308. keepalive: 30,
  2309. clientId: 'test',
  2310. username: 'username',
  2311. password: 42
  2312. })
  2313. testGenerateError('Username is required to use password', {
  2314. cmd: 'connect',
  2315. retain: false,
  2316. qos: 0,
  2317. dup: false,
  2318. length: 54,
  2319. protocolId: 'MQIsdp',
  2320. protocolVersion: 3,
  2321. will: {
  2322. retain: true,
  2323. qos: 2,
  2324. topic: 'topic',
  2325. payload: 'payload'
  2326. },
  2327. clean: true,
  2328. keepalive: 30,
  2329. clientId: 'test',
  2330. password: 'password'
  2331. })
  2332. testGenerateError('Invalid messageExpiryInterval: -4321', {
  2333. cmd: 'publish',
  2334. retain: true,
  2335. qos: 2,
  2336. dup: true,
  2337. length: 60,
  2338. topic: 'test',
  2339. payload: Buffer.from('test'),
  2340. messageId: 10,
  2341. properties: {
  2342. payloadFormatIndicator: true,
  2343. messageExpiryInterval: -4321,
  2344. topicAlias: 100,
  2345. responseTopic: 'topic',
  2346. correlationData: Buffer.from([1, 2, 3, 4]),
  2347. userProperties: {
  2348. test: 'test'
  2349. },
  2350. subscriptionIdentifier: 120,
  2351. contentType: 'test'
  2352. }
  2353. }, { protocolVersion: 5 })
  2354. testGenerateError('Invalid topicAlias: -100', {
  2355. cmd: 'publish',
  2356. retain: true,
  2357. qos: 2,
  2358. dup: true,
  2359. length: 60,
  2360. topic: 'test',
  2361. payload: Buffer.from('test'),
  2362. messageId: 10,
  2363. properties: {
  2364. payloadFormatIndicator: true,
  2365. messageExpiryInterval: 4321,
  2366. topicAlias: -100,
  2367. responseTopic: 'topic',
  2368. correlationData: Buffer.from([1, 2, 3, 4]),
  2369. userProperties: {
  2370. test: 'test'
  2371. },
  2372. subscriptionIdentifier: 120,
  2373. contentType: 'test'
  2374. }
  2375. }, { protocolVersion: 5 })
  2376. testGenerateError('Invalid subscriptionIdentifier: -120', {
  2377. cmd: 'publish',
  2378. retain: true,
  2379. qos: 2,
  2380. dup: true,
  2381. length: 60,
  2382. topic: 'test',
  2383. payload: Buffer.from('test'),
  2384. messageId: 10,
  2385. properties: {
  2386. payloadFormatIndicator: true,
  2387. messageExpiryInterval: 4321,
  2388. topicAlias: 100,
  2389. responseTopic: 'topic',
  2390. correlationData: Buffer.from([1, 2, 3, 4]),
  2391. userProperties: {
  2392. test: 'test'
  2393. },
  2394. subscriptionIdentifier: -120,
  2395. contentType: 'test'
  2396. }
  2397. }, { protocolVersion: 5 })
  2398. test('support cork', t => {
  2399. t.plan(9)
  2400. const dest = WS()
  2401. dest._write = (chunk, enc, cb) => {
  2402. t.pass('_write called')
  2403. cb()
  2404. }
  2405. mqtt.writeToStream({
  2406. cmd: 'connect',
  2407. retain: false,
  2408. qos: 0,
  2409. dup: false,
  2410. length: 18,
  2411. protocolId: 'MQIsdp',
  2412. protocolVersion: 3,
  2413. clean: false,
  2414. keepalive: 30,
  2415. clientId: 'test'
  2416. }, dest)
  2417. dest.end()
  2418. })
  2419. // The following test case was designed after experiencing errors
  2420. // when trying to connect with tls on a non tls mqtt port
  2421. // the specific behaviour is:
  2422. // - first byte suggests this is a connect message
  2423. // - second byte suggests message length to be smaller than buffer length
  2424. // thus payload processing starts
  2425. // - the first two bytes suggest a protocol identifier string length
  2426. // that leads the parser pointer close to the end of the buffer
  2427. // - when trying to read further connect flags the buffer produces
  2428. // a "out of range" Error
  2429. //
  2430. testParseError('Packet too short', Buffer.from([
  2431. 16, 9,
  2432. 0, 6,
  2433. 77, 81, 73, 115, 100, 112,
  2434. 3
  2435. ]))
  2436. // CONNECT Packets that show other protocol IDs than
  2437. // the valid values MQTT and MQIsdp should cause an error
  2438. // those packets are a hint that this is not a mqtt connection
  2439. testParseError('Invalid protocolId', Buffer.from([
  2440. 16, 18,
  2441. 0, 6,
  2442. 65, 65, 65, 65, 65, 65, // AAAAAA
  2443. 3, // Protocol version
  2444. 0, // Connect flags
  2445. 0, 10, // Keepalive
  2446. 0, 4, // Client ID length
  2447. 116, 101, 115, 116 // Client ID
  2448. ]))
  2449. // CONNECT Packets that contain an unsupported protocol version
  2450. // Flag (i.e. not `3` or `4` or '5') should cause an error
  2451. testParseError('Invalid protocol version', Buffer.from([
  2452. 16, 18,
  2453. 0, 6,
  2454. 77, 81, 73, 115, 100, 112, // Protocol ID
  2455. 1, // Protocol version
  2456. 0, // Connect flags
  2457. 0, 10, // Keepalive
  2458. 0, 4, // Client ID length
  2459. 116, 101, 115, 116 // Client ID
  2460. ]))
  2461. // When a packet contains a string in the variable header and the
  2462. // given string length of this exceeds the overall length of the packet that
  2463. // was specified in the fixed header, parsing must fail.
  2464. // this case simulates this behavior with the protocol ID string of the
  2465. // CONNECT packet. The fixed header suggests a remaining length of 8 bytes
  2466. // which would be exceeded by the string length of 15
  2467. // in this case, a protocol ID parse error is expected
  2468. testParseError('Cannot parse protocolId', Buffer.from([
  2469. 16, 8, // Fixed header
  2470. 0, 15, // string length 15 --> 15 > 8 --> error!
  2471. 77, 81, 73, 115, 100, 112,
  2472. 77, 81, 73, 115, 100, 112,
  2473. 77, 81, 73, 115, 100, 112,
  2474. 77, 81, 73, 115, 100, 112,
  2475. 77, 81, 73, 115, 100, 112,
  2476. 77, 81, 73, 115, 100, 112,
  2477. 77, 81, 73, 115, 100, 112,
  2478. 77, 81, 73, 115, 100, 112
  2479. ]))
  2480. testParseError('Unknown property', Buffer.from([
  2481. 61, 60, // Header
  2482. 0, 4, // Topic length
  2483. 116, 101, 115, 116, // Topic (test)
  2484. 0, 10, // Message ID
  2485. 47, // properties length
  2486. 126, 1, // unknown property
  2487. 2, 0, 0, 16, 225, // message expiry interval
  2488. 35, 0, 100, // topicAlias
  2489. 8, 0, 5, 116, 111, 112, 105, 99, // response topic
  2490. 9, 0, 4, 1, 2, 3, 4, // correlationData
  2491. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116, // userProperties
  2492. 11, 120, // subscriptionIdentifier
  2493. 3, 0, 4, 116, 101, 115, 116, // content type
  2494. 116, 101, 115, 116 // Payload (test)
  2495. ]), { protocolVersion: 5 })
  2496. testParseError('Not supported auth packet for this version MQTT', Buffer.from([
  2497. 240, 36, // Header
  2498. 0, // reason code
  2499. 34, // properties length
  2500. 21, 0, 4, 116, 101, 115, 116, // auth method
  2501. 22, 0, 4, 0, 1, 2, 3, // auth data
  2502. 31, 0, 4, 116, 101, 115, 116, // reasonString
  2503. 38, 0, 4, 116, 101, 115, 116, 0, 4, 116, 101, 115, 116 // userProperties
  2504. ]))
  2505. // When a Subscribe packet contains a topic_filter and the given
  2506. // length is topic_filter.length + 1 then the last byte (requested QoS) is interpreted as topic_filter
  2507. // reading the requested_qos at the end causes 'Index out of range' read
  2508. testParseError('Malformed Subscribe Payload', Buffer.from([
  2509. 130, 14, // subscribe header and remaining length
  2510. 0, 123, // packet ID
  2511. 0, 10, // topic filter length
  2512. 104, 105, 106, 107, 108, 47, 109, 110, 111, // topic filter with length of 9 bytes
  2513. 0 // requested QoS
  2514. ]))
  2515. testWriteToStreamError('Invalid command', {
  2516. cmd: 'invalid'
  2517. })
  2518. testWriteToStreamError('Invalid protocolId', {
  2519. cmd: 'connect',
  2520. protocolId: {}
  2521. })
  2522. test('userProperties null prototype', t => {
  2523. t.plan(3)
  2524. const packet = mqtt.generate({
  2525. cmd: 'connect',
  2526. retain: false,
  2527. qos: 0,
  2528. dup: false,
  2529. length: 125,
  2530. protocolId: 'MQTT',
  2531. protocolVersion: 5,
  2532. will: {
  2533. retain: true,
  2534. qos: 2,
  2535. properties: {
  2536. willDelayInterval: 1234,
  2537. payloadFormatIndicator: false,
  2538. messageExpiryInterval: 4321,
  2539. contentType: 'test',
  2540. responseTopic: 'topic',
  2541. correlationData: Buffer.from([1, 2, 3, 4]),
  2542. userProperties: {
  2543. test: 'test'
  2544. }
  2545. },
  2546. topic: 'topic',
  2547. payload: Buffer.from([4, 3, 2, 1])
  2548. },
  2549. clean: true,
  2550. keepalive: 30,
  2551. properties: {
  2552. sessionExpiryInterval: 1234,
  2553. receiveMaximum: 432,
  2554. maximumPacketSize: 100,
  2555. topicAliasMaximum: 456,
  2556. requestResponseInformation: true,
  2557. requestProblemInformation: true,
  2558. userProperties: {
  2559. test: 'test'
  2560. },
  2561. authenticationMethod: 'test',
  2562. authenticationData: Buffer.from([1, 2, 3, 4])
  2563. },
  2564. clientId: 'test'
  2565. })
  2566. const parser = mqtt.parser()
  2567. parser.on('packet', packet => {
  2568. t.equal(packet.cmd, 'connect')
  2569. t.equal(Object.getPrototypeOf(packet.properties.userProperties), null)
  2570. t.equal(Object.getPrototypeOf(packet.will.properties.userProperties), null)
  2571. })
  2572. parser.parse(packet)
  2573. })
  2574. test('stops parsing after first error', t => {
  2575. t.plan(4)
  2576. const parser = mqtt.parser()
  2577. let packetCount = 0
  2578. let errorCount = 0
  2579. let expectedPackets = 1
  2580. let expectedErrors = 1
  2581. parser.on('packet', packet => {
  2582. t.ok(++packetCount <= expectedPackets, `expected <= ${expectedPackets} packets`)
  2583. })
  2584. parser.on('error', erroneous => {
  2585. t.ok(++errorCount <= expectedErrors, `expected <= ${expectedErrors} errors`)
  2586. })
  2587. parser.parse(Buffer.from([
  2588. // First, a valid connect packet:
  2589. 16, 12, // Header
  2590. 0, 4, // Protocol ID length
  2591. 77, 81, 84, 84, // Protocol ID
  2592. 4, // Protocol version
  2593. 2, // Connect flags
  2594. 0, 30, // Keepalive
  2595. 0, 0, // Client ID length
  2596. // Then an invalid subscribe packet:
  2597. 128, 9, // Header (subscribeqos=0length=9)
  2598. 0, 6, // Message ID (6)
  2599. 0, 4, // Topic length,
  2600. 116, 101, 115, 116, // Topic (test)
  2601. 0, // Qos (0)
  2602. // And another invalid subscribe packet:
  2603. 128, 9, // Header (subscribeqos=0length=9)
  2604. 0, 6, // Message ID (6)
  2605. 0, 4, // Topic length,
  2606. 116, 101, 115, 116, // Topic (test)
  2607. 0, // Qos (0)
  2608. // Finally, a valid disconnect packet:
  2609. 224, 0 // Header
  2610. ]))
  2611. // Calling parse again clears the error and continues parsing
  2612. packetCount = 0
  2613. errorCount = 0
  2614. expectedPackets = 2
  2615. expectedErrors = 0
  2616. parser.parse(Buffer.from([
  2617. // Connect:
  2618. 16, 12, // Header
  2619. 0, 4, // Protocol ID length
  2620. 77, 81, 84, 84, // Protocol ID
  2621. 4, // Protocol version
  2622. 2, // Connect flags
  2623. 0, 30, // Keepalive
  2624. 0, 0, // Client ID length
  2625. // Disconnect:
  2626. 224, 0 // Header
  2627. ]))
  2628. })
  2629. testGenerateErrorMultipleCmds([
  2630. 'publish',
  2631. 'puback',
  2632. 'pubrec',
  2633. 'pubrel',
  2634. 'subscribe',
  2635. 'suback',
  2636. 'unsubscribe',
  2637. 'unsuback'
  2638. ], 'Invalid messageId', {
  2639. qos: 1, // required for publish
  2640. topic: 'test', // required for publish
  2641. messageId: 'a'
  2642. }, {})