1 module doap.protocol.packet;
2 
3 import doap.protocol.types : MessageType;
4 import doap.protocol.codes : Code;
5 import doap.exceptions : CoapException;
6 import std.conv : to;
7 import doap.utils : order, Order;
8 
9 /** 
10  * Payload marker
11  */
12 private ubyte PAYLOAD_MARKER = cast(ubyte)-1;
13 
14 /** 
15  * A header option
16  */
17 public struct CoapOption
18 {
19     /** 
20      * Option ID
21      */
22     public ushort id;
23 
24     /** 
25      * Option value
26      */
27     public ubyte[] value;
28 }
29 
30 
31 // TODO: remove this
32 import std.stdio : writeln;
33 
34 
35 private enum OptionLenType
36 {
37     ZERO_TO_TWELVE,
38     _8BIT_EXTENDED,
39     _12_BIT_EXTENDED,
40     UPPER_PAYLOAD_MARKER
41 }
42 
43 private enum OptionDeltaType
44 {
45     ZERO_TO_TWELVE,
46     _8BIT_EXTENDED,
47     _12_BIT_EXTENDED,
48     UPPER_PAYLOAD_MARKER
49 }
50 
51 
52 /** 
53  * Represents a CoAP packet
54  */
55 public class CoapPacket
56 {
57     /** 
58      * The protocol version
59      */
60     private ubyte ver;
61 
62     /** 
63      * The message type
64      */
65     private MessageType type;
66 
67     /** 
68      * Token length
69      */
70     private ubyte tokenLen;
71 
72     /** 
73      * The code
74      */
75     private Code code;
76 
77     /** 
78      * The message id (mid)
79      */
80     private ushort mid;
81 
82     /** 
83      * The token (if any)
84      */
85     private ubyte[] token;
86 
87     /** 
88      * List of options (if any)
89      */
90     private CoapOption[] options;
91 
92     /** 
93      * The payload (if any)
94      */
95     private ubyte[] payload;
96 
97     /** 
98      * Constructs a new `CoapPacket` 
99      * with protocol version 1
100      */
101     this()
102     {
103         // Set the version (Default is 1)
104         ver = 1;
105     }
106 
107     /** 
108      * Encodes the current `CoapPacket`
109      * into the byte sequence
110      *
111      * Returns: the bytes
112      */
113     public ubyte[] getBytes()
114     {
115         ubyte[] encoded;
116 
117         // Calculate the first byte (ver | type | tkl)
118         ubyte firstByte = cast(ubyte)(ver << 6);
119         firstByte = firstByte | cast(ubyte)(type << 4);
120         firstByte = firstByte | tokenLen;
121         encoded ~= firstByte;
122 
123         // Set the request/response code
124         encoded ~= code;
125 
126         // Set the message ID (encoded as big endian)
127         version(LittleEndian)
128         {
129             ubyte* basePtr = cast(ubyte*)&mid;
130             ubyte lowByte = *basePtr;
131             ubyte hiByte = *(basePtr+1);
132 
133             encoded ~= [hiByte, lowByte];
134 
135         }
136         else version(BigEndian)
137         {
138             ubyte* basePtr = cast(ubyte*)&mid;
139             ubyte lowByte = *(basePtr+1);
140             ubyte hiByte = *(basePtr);
141             encoded ~= [hiByte, lowByte];
142         }
143 
144         // Set the token (if any)
145         if(tokenLen)
146         {
147             encoded ~= token;
148         }
149 
150         // FIXME: Add options encoding
151         foreach(CoapOption option; orderOptions())
152         {
153             encoded ~= encodeOption(option);
154         }
155 
156         // Set the payload marker
157         encoded ~= PAYLOAD_MARKER;
158 
159         // Set the payload
160         encoded ~= payload;
161         
162         return encoded;
163     }
164 
165     // TODO: Make public in the future
166     private static ubyte[] encodeOption(CoapOption option)
167     {
168         // TODO: Implement this
169         return [];
170     }
171 
172     private CoapOption[] orderOptions()
173     {
174         // TODO: Implement ordering here
175         return this.options;
176     }
177 
178     /** 
179      * Given a payload size this determines
180      * the required type of option length
181      * encoding to be used.
182      *
183      * If the size is unsupported then
184      * `OptionLenType.UPPER_PAYLOAD_MARKER`
185      * is returned.
186      *
187      * Params:
188      *   dataSize = the payload's size
189      * Returns: the `OptionLenType`
190      */
191     private static OptionLenType determineLenType(size_t dataSize)
192     {
193         if(dataSize >= 0 && dataSize <= 12)
194         {
195             return OptionLenType.ZERO_TO_TWELVE;
196         }
197         else if(dataSize >= 13 && dataSize <= 268)
198         {
199             return OptionLenType._8BIT_EXTENDED;
200         }
201         else if(dataSize >= 269 && dataSize <= 65804)
202         {
203             return OptionLenType._12_BIT_EXTENDED;
204         }
205         else
206         {
207             return OptionLenType.UPPER_PAYLOAD_MARKER;
208         }
209     }
210 
211     /** 
212      * Given an option ID this determines
213      * the required type of option id
214      * encoding to be used.
215      *
216      * If the size is unsupported then
217      * `OptionLenType.UPPER_PAYLOAD_MARKER`
218      * is returned.
219      *
220      * Params:
221      *   id = the option id
222      * Returns: the `OptionDeltaType`
223      */
224     private static OptionDeltaType determineOptionType(size_t id)
225     {
226         if(id >= 0 && id <= 12)
227         {
228             return OptionDeltaType.ZERO_TO_TWELVE;
229         }
230         else if(id >= 13 && id <= 268)
231         {
232             return OptionDeltaType._8BIT_EXTENDED;
233         }
234         else if(id >= 269 && id <= 65804)
235         {
236             return OptionDeltaType._12_BIT_EXTENDED;
237         }
238         else
239         {
240             return OptionDeltaType.UPPER_PAYLOAD_MARKER;
241         }
242     }
243 
244     /** 
245      * Sets the message type
246      *
247      * Params:
248      *   type = the `MessageType`
249      */
250     public void setType(MessageType type)
251     {
252         this.type = type;
253     }
254 
255     /** 
256      * Sets the token
257      *
258      * Params:
259      *   token = the token to set
260      * Throws:
261      *   CoapException if the token
262      * is too long
263      */
264     public void setToken(ubyte[] token)
265     {
266         if(setTokenLength(token.length))
267         {
268             this.token = token;
269         }
270         else
271         {
272             throw new CoapException("Token length above 15 bytes not allowed");
273         }
274     }
275 
276     /** 
277      * Attempts to set the token length
278      * and returns whether or not it
279      * was a success
280      *
281      * Params:
282      *   tkl = the token length
283      * Returns: `true` if a valid lenght,
284      * `false` otherwise
285      */
286     private bool setTokenLength(ulong tkl)
287     {
288         if(tkl > 15)
289         {
290             return false;
291         }
292         else
293         {
294             this.tokenLen = cast(ubyte)tkl;
295             return true;    
296         }
297     }
298 
299     /** 
300      * Sets the code
301      *
302      * Params:
303      *   code = the `Code`
304      */
305     public void setCode(Code code)
306     {
307         this.code = code;
308     }
309 
310     /** 
311      * Sets the message ID
312      *
313      * Params:
314      *   mid = the message id
315      */
316     public void setMessageId(ushort mid)
317     {
318         this.mid = mid;
319     }
320 
321     public void setOptions()
322     {
323         // FIXME: Implement me
324     }
325 
326     /** 
327      * Sets the payload for this
328      * message
329      *
330      * Params:
331      *   payload = the payload
332      */
333     public void setPayload(ubyte[] payload)
334     {
335         this.payload = payload;
336     }
337 
338     /** 
339      * Returns the protocol version
340      *
341      * Returns: the version
342      */
343     public ubyte getVersion()
344     {
345         return this.ver;
346     }
347 
348     /** 
349      * Returns the type of message
350      *
351      * Returns: the `MessageType`
352      */
353     public MessageType getType()
354     {
355         return this.type;
356     }
357 
358     /** 
359      * Returns the length of the currently
360      * set token
361      *
362      * Returns: the length
363      */
364     public ubyte getTokenLength()
365     {
366         return this.tokenLen;
367     }
368 
369     /** 
370      * Returns the token
371      *
372      * Returns: the token
373      */
374     public ubyte[] getToken()
375     {
376         return this.token;
377     }
378 
379     /** 
380      * Returns the code
381      *
382      * Returns: the `Code`
383      */
384     public Code getCode()
385     {
386         return this.code;
387     }
388 
389     /** 
390      * Returns the message id
391      *
392      * Returns: the message id
393      */
394     public ushort getMessageId()
395     {
396         return this.mid;
397     }
398 
399     /** 
400      * Decodes the given bytes into a `CoapPacket`
401      *
402      * Params:
403      *   data = the bytes to decode
404      * Returns: a decoded `CoapPacket`
405      * Throws:
406      *   CoapException on error decoding
407      */
408     public static CoapPacket fromBytes(ubyte[] data)
409     {
410         CoapPacket packet = new CoapPacket();
411 
412         if(data.length < 4)
413         {
414             throw new CoapException("CoAP message must be at least 4 bytes in size");
415         }
416 
417         packet.ver = data[0]>>6;
418         packet.type = cast(MessageType)( (data[0]>>4) & 3);
419         packet.tokenLen = data[0]&15;
420 
421         packet.code = cast(Code)(data[1]);
422         writeln("Decoded code: ", packet.code);
423 
424 
425         ubyte* midBase = data[2..4].ptr;
426         version(LittleEndian)
427         {
428             ubyte* pMidBase = cast(ubyte*)&packet.mid;
429             *(pMidBase) = *(midBase+1);
430             *(pMidBase+1) = *(midBase);
431         }
432         else version(BigEndian)
433         {
434             ubyte* pMidBase = cast(ubyte*)&packet.mid;
435             *(pMidBase) = *(midBase);
436             *(pMidBase+1) = *(midBase+1);
437         }
438 
439         if(packet.tokenLen)
440         {
441             packet.token = data[4..4+packet.tokenLen];
442         }
443 
444         // TODO: Do options decode here
445         ubyte[] remainder = data[4+packet.tokenLen..$];
446         version(unittest) writeln("Remainder: ", remainder);
447 
448         ulong idx = 4+packet.tokenLen;
449 
450         writeln();
451         writeln();
452 
453         CoapOption[] createdOptions;
454         if(remainder.length)
455         {
456             // import std.container.slist : SList;
457             // SList!(CoapOption) createdOptions;
458 
459             // First "previous" delta is 0
460             ushort delta = 0;
461 
462             ushort curOptionNumber;
463             while(true)
464             {
465                 writeln("Delta (ENTER): ", delta);
466                 writeln("Remainder [from-idx..$] (ENTER): ", data[idx..$]);
467 
468                 scope(exit)
469                 {
470                     writeln("Currently built options: ", createdOptions);
471                     writeln();
472                     writeln();
473                 }
474 
475                 ubyte curValue = data[idx];
476 
477                 // If entire current value is -1/~0
478                 // then we reached the payload marker
479                 if(curValue == PAYLOAD_MARKER)
480                 {
481                     writeln("Found payload marker, stopping option parsing");
482                     idx++;
483                     break;
484 
485                 }
486 
487 
488                 ubyte computed = (curValue&240) >> 4;
489                 writeln("Computed delta: ", computed);
490 
491                 // 0 to 12 Option ID
492                 if(computed >= 0 && computed <= 12)
493                 {
494                     writeln("Delta is 0 to 12");
495 
496                     // In such a case the delta we add on is this 4 bit eneity
497                     delta+=computed;
498                     writeln("Option id: ", delta);
499 
500 
501                     // Get the type of option length
502                     OptionLenType optLenType = getOptionLenType(curValue);
503                     writeln("Option length type: ", optLenType);
504 
505                     // Simple case (12)
506                     if(optLenType == OptionLenType.ZERO_TO_TWELVE)
507                     {
508                         // Compute the length
509                         ubyte optLen = (curValue&15);
510                         writeln("Option len: ", optLen);
511 
512                         // Update idx to jump over the (option delta | option length)
513                         idx+=1;
514 
515                         // Grab the data from [idx, idx+length)
516                         ubyte[] optionValue = data[idx..idx+optLen];
517                         writeln("Option value: ", optionValue);
518 
519                         // Jump over the option value
520                         idx+=optLen;
521 
522                         // Create the option and add it to the list of options
523                         CoapOption option;
524                         option.value = optionValue;
525                         option.id = delta;
526                         writeln("Built option: ", option);
527                         createdOptions ~= option;
528                     }
529                     // Option length extended (8bit) (13)
530                     else if(optLenType == OptionLenType._8BIT_EXTENDED)
531                     {
532                         // Next byte has the length
533                         idx+=1;
534 
535                         // The total length is the extended value (which lacks 13 so we must add it)
536                         writeln(data[idx..$]);
537                         ubyte optLen8BitExt = data[idx];
538                         ushort optLen = optLen8BitExt+13;
539                         writeln("Option len: ", optLen);
540                         
541                         // Jump over 8bit opt len ext
542                         idx+=1;
543 
544                         // Grab the data from [idx, idx+optLen)
545                         ubyte[] optionValue = data[idx..idx+optLen];
546                         writeln("Option value: ", optionValue);
547                         writeln("Option value: ", cast(string)optionValue);
548 
549                         // Jump over the option value
550                         idx+=optLen;
551 
552                         // Create the option and add it to the list of options
553                         CoapOption option;
554                         option.value = optionValue;
555                         option.id = delta;
556                         writeln("Built option: ", option);
557                         createdOptions ~= option;
558                     }
559                     // Option length extended (16bit) (14)
560                     else if(optLenType == OptionLenType._12_BIT_EXTENDED)
561                     {
562                         // TODO: THIS IS UNTESTED CODE!!!
563 
564                         // Jump to next byte of two bytes (which has length)
565                         idx+=1;
566 
567                         // Option length compute (it lacks 269 so add it back)
568                         ushort optLen = order(*cast(ushort*)&data[idx], Order.BE);
569                         optLen+=269;
570                         writeln("Option len: ", optLen);
571 
572                         // Jump over the two option length bytes
573                         idx+=2;
574 
575                         // Grab the data from [idx, idx+optLen)
576                         ubyte[] optionValue = data[idx..idx+optLen];
577                         writeln("Option value: ", optionValue);
578                         writeln("Option value: ", cast(string)optionValue);
579 
580                         // Jump over the option value
581                         idx+=optLen;
582 
583                         // Create the option and add it to the list of options
584                         CoapOption option;
585                         option.value = optionValue;
586                         option.id = delta;
587                         writeln("Built option: ", option);
588                         createdOptions ~= option;
589                     }
590                 }
591                 // 13
592                 else if(computed == 13)
593                 {
594                     writeln("3333 Option delta type: 13 - DEVELOPER ADD SUPPORT! 3333");
595 
596                     // TODO: This is UNTESTED code!!!!
597 
598                     // Skip over the 4bit tuple
599                     idx+=1;
600 
601                     // Delta value is 1 byte (the value found is lacking 13 so add it back)
602                     ubyte deltaAddition = data[idx];
603                     deltaAddition+=13;
604 
605                     // Update delta
606                     delta+=deltaAddition;
607 
608                     // Our option ID is then calculated from the current delta
609                     ushort optionId = delta;
610 
611                     // Jump over the 1 byte option delta
612                     idx+=1;
613 
614                     writeln("8 bit option-id delta: ", optionId);
615 
616                     // Get the type of option length
617                     OptionLenType optLenType = getOptionLenType(curValue);
618                     writeln("Option length type: ", optLenType);
619 
620                     // Simple case (12)
621                     if(optLenType == OptionLenType.ZERO_TO_TWELVE)
622                     {
623                         // Compute the length
624                         ubyte optLen = (curValue&15);
625                         writeln("Option len: ", optLen);
626 
627                         // Grab the data from [idx, idx+length)
628                         ubyte[] optionValue = data[idx..idx+optLen];
629                         writeln("Option value: ", optionValue);
630 
631                         // Jump over the option value
632                         idx+=optLen;
633 
634                         // Create the option and add it to the list of options
635                         CoapOption option;
636                         option.value = optionValue;
637                         option.id = delta;
638                         writeln("Built option: ", option);
639                         createdOptions ~= option;
640                     }
641                     // Option length extended (8bit) (13)
642                     else if(optLenType == OptionLenType._8BIT_EXTENDED)
643                     {
644                         // The total length is the extended value (which lacks 13 so we must add it)
645                         writeln(data[idx..$]);
646                         ubyte optLen8BitExt = data[idx];
647                         ushort optLen = optLen8BitExt+13;
648                         writeln("Option len: ", optLen);
649                         
650                         // Jump over 8bit opt len ext
651                         idx+=1;
652 
653                         // Grab the data from [idx, idx+optLen)
654                         ubyte[] optionValue = data[idx..idx+optLen];
655                         writeln("Option value: ", optionValue);
656                         writeln("Option value: ", cast(string)optionValue);
657 
658                         // Jump over the option value
659                         idx+=optLen;
660 
661                         // Create the option and add it to the list of options
662                         CoapOption option;
663                         option.value = optionValue;
664                         option.id = delta;
665                         writeln("Built option: ", option);
666                         createdOptions ~= option;
667                     }
668                     // Option length extended (16bit) (14)
669                     else if(optLenType == OptionLenType._12_BIT_EXTENDED)
670                     {
671                         // Option length compute (it lacks 269 so add it back)
672                         ushort optLen = order(*cast(ushort*)&data[idx], Order.BE);
673                         optLen+=269;
674                         writeln("Option len: ", optLen);
675 
676                         // Jump over the two option length bytes
677                         idx+=2;
678 
679                         // Grab the data from [idx, idx+optLen)
680                         ubyte[] optionValue = data[idx..idx+optLen];
681                         writeln("Option value: ", optionValue);
682                         writeln("Option value: ", cast(string)optionValue);
683 
684                         // Jump over the option value
685                         idx+=optLen;
686 
687                         // Create the option and add it to the list of options
688                         CoapOption option;
689                         option.value = optionValue;
690                         option.id = delta;
691                         writeln("Built option: ", option);
692                         createdOptions ~= option;
693                     }
694                 }
695                 // 14
696                 else if(computed == 14)
697                 {
698                     writeln("Option delta type: 14 - DEVELOPER ADD SUPPORT!");
699 
700                     // Skip over 4bit tuple
701                     idx+=1;
702 
703                     // Delta value is 2 bytes (BE)
704                     ubyte[] optionIdBytes = data[idx..idx+2];
705                     ushort unProcessedValue = *(cast(ushort*)optionIdBytes.ptr);
706 
707                     // The value found is then lacking 269 (so add it back)
708                     ushort deltaAddition = order(unProcessedValue, Order.BE);
709                     deltaAddition+=269;
710 
711                     // Update delta
712                     delta+=deltaAddition;
713 
714                     // Our option ID is then calculated from the current delta
715                     ushort optionId = delta;
716 
717                     // Jump over [Option delta extended (16bit)] here
718                     idx+=2;
719 
720                     writeln("16 bit option-id delta: ", optionId);
721 
722                     // Get the option length type
723                     OptionLenType optLenType = getOptionLenType(curValue);
724                     writeln("Option len type: ", optLenType);
725 
726                     // 0 to 12 length type
727                     if(optLenType == OptionLenType.ZERO_TO_TWELVE)
728                     {
729                         // Option length
730                         ubyte optLen = (curValue&15);
731                         writeln("Option len: ", optLen);
732 
733                         // Read the option now
734                         ubyte[] optionValue = data[idx..idx+optLen];
735 
736                         // Jump over the option value
737                         idx+=optLen;
738 
739                         // Create the option and add it to the list of options
740                         CoapOption option;
741                         option.value = optionValue;
742                         option.id = optionId;
743                         writeln("Built option: ", option);
744                         createdOptions ~= option;
745                     }
746                     // Option length extended (8bit) (13)
747                     else if(optLenType == OptionLenType._8BIT_EXTENDED)
748                     {
749                         // TODO: THIS IS UNTESTED CODE!!!!
750 
751                         // The total length is the extended value (which lacks 13 so we must add it)
752                         writeln(data[idx..$]);
753                         ubyte optLen8BitExt = data[idx];
754                         ushort optLen = optLen8BitExt+13;
755                         writeln("Option len: ", optLen);
756                         
757                         // Jump over 8bit opt len ext
758                         idx+=1;
759 
760                         // Grab the data from [idx, idx+optLen)
761                         ubyte[] optionValue = data[idx..idx+optLen];
762                         writeln("Option value: ", optionValue);
763                         writeln("Option value: ", cast(string)optionValue);
764 
765                         // Jump over the option value
766                         idx+=optLen;
767 
768                         // Create the option and add it to the list of options
769                         CoapOption option;
770                         option.value = optionValue;
771                         option.id = delta;
772                         writeln("Built option: ", option);
773                         createdOptions ~= option;
774                     }
775                     // Option length extended (16bit) (14)
776                     else if(optLenType == OptionLenType._12_BIT_EXTENDED)
777                     {
778                         // TODO: THIS IS UNTESTED CODE!!!!
779 
780                         // Option length compute (it lacks 269 so add it back)
781                         ushort optLen = order(*cast(ushort*)&data[idx], Order.BE);
782                         optLen+=269;
783                         writeln("Option len: ", optLen);
784 
785                         // Jump over the two option length bytes
786                         idx+=2;
787 
788                         // Grab the data from [idx, idx+optLen)
789                         ubyte[] optionValue = data[idx..idx+optLen];
790                         writeln("Option value: ", optionValue);
791                         writeln("Option value: ", cast(string)optionValue);
792 
793                         // Jump over the option value
794                         idx+=optLen;
795 
796                         // Create the option and add it to the list of options
797                         CoapOption option;
798                         option.value = optionValue;
799                         option.id = delta;
800                         writeln("Built option: ", option);
801                         createdOptions ~= option;
802                     }
803                     else
804                     {
805                         writeln("OptionDelta14 Mode: We don't yet support other option lengths in this mode");
806                         assert(false);
807                     }
808 
809 
810 
811                     
812                     
813                     // Move onto the first byte of the next two (16 bit BE option-length extended)
814 
815                     writeln("Support not yet finished for delta type 14");
816 
817                     // break;
818                 }
819                 // 15
820                 else if(computed == 15)
821                 {
822                     writeln("FIVEFIVEFIVE Option delta type: 15 - DEVELOPER ADD SUPPORT! FIVEFIVEFIVE");
823                     assert(false);
824                 }
825                 else
826                 {
827                     assert(false);
828                 }
829 
830                 // break;
831             }
832         }
833 
834         packet.options = createdOptions;
835 
836 
837 
838         return packet;
839     }
840 
841     /** 
842      * Extracts the option length encoding
843      * type from the header
844      *
845      * Params:
846      *   hdr = the header
847      * Returns: the `OptionLenType`
848      */
849     private static OptionLenType getOptionLenType(ubyte hdr)
850     {
851         ubyte type = (hdr&15);
852         if(type >= 0 && type <= 12)
853         {
854             return OptionLenType.ZERO_TO_TWELVE;
855         }
856         else if(type == 13)
857         {
858             return OptionLenType._8BIT_EXTENDED;
859         }
860         else if(type == 14)
861         {
862             return OptionLenType._12_BIT_EXTENDED;
863         }
864         else
865         {
866             return OptionLenType.UPPER_PAYLOAD_MARKER;
867         }
868     }
869 
870     /** 
871      * Returns a string represenation
872      * of this packet
873      *
874      * Returns: the string
875      */
876     public override string toString()
877     {
878         return "CoapPacket [ver: "~to!(string)(ver)~
879                             ", type: "~to!(string)(type)~
880                             ", tkl: "~to!(string)(tokenLen)~
881                             ", code: "~to!(string)(code)~
882                             ", mid: "~to!(string)(mid)~
883                             ", token: "~to!(string)(token)~
884                             ", options: "~to!(string)(options)~
885                             "]";
886     }
887 }
888 
889 version(unittest)
890 {
891     import std.stdio;
892 }
893 
894 /**
895  * Tests `CoapPacket`'s `determineLenType(size_t)'
896  */
897 unittest
898 {
899     assert(CoapPacket.determineLenType(12) == OptionLenType.ZERO_TO_TWELVE);
900     assert(CoapPacket.determineLenType(268) == OptionLenType._8BIT_EXTENDED);
901     assert(CoapPacket.determineLenType(65804) == OptionLenType._12_BIT_EXTENDED);
902     assert(CoapPacket.determineLenType(65804+1) == OptionLenType.UPPER_PAYLOAD_MARKER);
903 }
904 
905 /**
906  * Tests `CoapPacket`'s `determineOptionType(size_t)'
907  */
908 unittest
909 {
910     assert(CoapPacket.determineOptionType(12) == OptionDeltaType.ZERO_TO_TWELVE);
911     assert(CoapPacket.determineOptionType(268) == OptionDeltaType._8BIT_EXTENDED);
912     assert(CoapPacket.determineOptionType(65804) == OptionDeltaType._12_BIT_EXTENDED);
913     assert(CoapPacket.determineOptionType(65804+1) == OptionDeltaType.UPPER_PAYLOAD_MARKER);
914 }
915 
916 /**
917  * Encoding tests
918  *
919  * These set high level parameters and then
920  * we call `getBytes()` and analyse the components
921  * of the encoded wire format by hand to ensure
922  * they are set in place correctly
923  */
924 unittest
925 {
926     CoapPacket packet = new CoapPacket();
927 
928     packet.setType(MessageType.RESET);
929 
930     ubyte[] token = [0, 69];
931     packet.setToken(token);
932 
933     packet.setCode(Code.PONG);
934 
935     packet.setMessageId(257);
936 
937     // FIXME: Set options
938 
939     ubyte[] payload = cast(ubyte[])[-1, -2];
940     writeln(payload.length);
941     packet.setPayload(payload);
942 
943 
944 
945     ubyte[] encoded = packet.getBytes();
946 
947     ubyte firstByte = encoded[0];
948 
949     // Ensure the version is set to 1
950     ubyte versionField = cast(ubyte)(firstByte & 192) >> 6;
951     assert(versionField == 1);
952 
953     // Ensure the type is 3/RESET
954     ubyte typeField = cast(ubyte)(firstByte & 48) >> 4;
955     assert(typeField == MessageType.RESET);
956 
957     // Ensure the token length is 2
958     ubyte tklField = firstByte & 15;
959     assert(tklField == token.length);
960 
961     ubyte secondByte = encoded[1];
962 
963     // Ensure the code is set to PONG
964     // Class is 7
965     // Code is 3
966     ubyte codeClass = cast(ubyte)(secondByte & 224)  >> 5;
967     assert(codeClass == 7);
968     ubyte code = (secondByte & (~224));
969     assert(code == 3);
970     writeln(codeClass);
971     writeln(code);
972     assert(secondByte == Code.PONG);
973 
974     // Ensure the message ID is 257
975     ubyte thirdByte = encoded[2], fourthByte = encoded[3];
976     assert(thirdByte == 1);
977     assert(fourthByte == 1);
978 
979     // Ensure the token is [0, 69]
980     ubyte fifthByte = encoded[4], sixthByte = encoded[5];
981     assert(fifthByte == 0);
982     assert(sixthByte == 69);
983 
984     // FIXME: Ensure options
985 
986     // Ensure the payload marker is here
987     ubyte seventhByte = encoded[6];
988     assert(seventhByte == PAYLOAD_MARKER);
989 
990     // Ensure the payload is [255, 254]
991     // FIXME: Offset because of options later
992     ubyte eighthByte = encoded[7], ninthByte = encoded[8];
993     assert(eighthByte == 255);
994     assert(ninthByte == 254);
995 
996 }
997 
998 /**
999  * Decoding tests
1000  *
1001  * These tests take a byte array of an encoded
1002  * CoAP packet and then decodes it into a new
1003  * `CoapPacket` object
1004  */
1005 unittest
1006 {
1007     // Version: 1 | Type: RESET (3) : TLK: 0
1008     // Code: 2 (POST) | MID: 257
1009     ubyte[] packetData = [112, 2, 1, 1];
1010 
1011     CoapPacket packet = CoapPacket.fromBytes(packetData);
1012 
1013     assert(packet.getVersion() == 1);
1014     assert(packet.getType() == MessageType.RESET);
1015     assert(packet.getTokenLength() == 0);
1016     assert(packet.getCode() == Code.POST);
1017     // TODO: Add message ID check + token check
1018     assert(packet.getMessageId() == 257);
1019 }
1020 
1021 unittest
1022 {
1023     writeln("Begin big coap test (lekker real life)\n\n");
1024 
1025     ubyte[] testingIn = [
1026                          0x41, 0x02, 0xcd, 0x47, 0x45, 0x3d,
1027                          0x03, 0x31, 0x30, 0x30, 0x2e, 0x36, 0x34, 0x2e,
1028                          0x30, 0x2e, 0x31, 0x32, 0x3a, 0x35,
1029                          0x36, 0x38, 0x33, 0x92, 0x27, 0x11, 0xe1, 0xfc,
1030                          0xd0, 0x01, 0x21, 0x10, 0x21, 0x01,
1031                          0xff, 0xc0, 0x01, 0xc1, 0x00, 0x0f, 0x00, 0x00,
1032                          0x28, 0x00, 0x00, 0xff, 0x02, 0x00
1033                         ];
1034 
1035 
1036     CoapPacket packet = CoapPacket.fromBytes(testingIn);
1037     writeln(packet);
1038 }
1039 
1040 /**
1041  * Tests the minimum size required for a packet
1042  * (Negative case)
1043  */
1044 unittest
1045 {
1046     ubyte[] testingIn = [];
1047 
1048     try
1049     {
1050         CoapPacket packet = CoapPacket.fromBytes(testingIn);
1051         assert(false);
1052     }
1053     catch(CoapException e)
1054     {
1055         assert(true);
1056     }
1057 
1058     testingIn = [ 0x41, 0x02, 0xcd];
1059 
1060     try
1061     {
1062         CoapPacket packet = CoapPacket.fromBytes(testingIn);
1063         assert(false);
1064     }
1065     catch(CoapException e)
1066     {
1067         assert(true);
1068     }
1069 }
1070 
1071 /**
1072  * Tests the minimum size required for a packet
1073  * (Positive case)
1074  */
1075 unittest
1076 {
1077     // FIXME: Actually make a better example
1078     // ubyte[] testingIn = [0x41, 0x02, 0xcd, 0x47];
1079 
1080     // try
1081     // {
1082     //     CoapPacket packet = CoapPacket.fromBytes(testingIn);
1083 
1084     //     // TODO: Test
1085     //     assert(true);
1086     // }
1087     // catch(CoapException e)
1088     // {
1089     //     assert(false);
1090     // }
1091 }