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*)∣ 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*)∣ 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 }