test.js 79 KB

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