001/* 002 * Copyright (C) 2015-2016 Neo Visionaries Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package NeoVisionaries.WebSockets; 017 018 019import static NeoVisionaries.WebSockets.WebSocketOpcode.BINARY; 020import static NeoVisionaries.WebSockets.WebSocketOpcode.CLOSE; 021import static NeoVisionaries.WebSockets.WebSocketOpcode.CONTINUATION; 022import static NeoVisionaries.WebSockets.WebSocketOpcode.PING; 023import static NeoVisionaries.WebSockets.WebSocketOpcode.PONG; 024import static NeoVisionaries.WebSockets.WebSocketOpcode.TEXT; 025import java.util.ArrayList; 026import java.util.Arrays; 027import java.util.List; 028 029 030/** 031 * WebSocket frame. 032 * 033 * <EMBED CLASS='external-html' DATA-FILE-ID=LICENSE> 034 * 035 * @see <a href="https://tools.ietf.org/html/rfc6455#section-5" 036 * >RFC 6455, 5. Data Framing</a> 037 */ 038public class WebSocketFrame 039{ 040 private boolean mFin; 041 private boolean mRsv1; 042 private boolean mRsv2; 043 private boolean mRsv3; 044 private int mOpcode; 045 private boolean mMask; 046 private byte[] mPayload; 047 048 049 /** 050 * Get the value of FIN bit. 051 * 052 * @return 053 * The value of FIN bit. 054 */ 055 public boolean getFin() 056 { 057 return mFin; 058 } 059 060 061 /** 062 * Set the value of FIN bit. 063 * 064 * @param fin 065 * The value of FIN bit. 066 * 067 * @return 068 * {@code this} object. 069 */ 070 public WebSocketFrame setFin(boolean fin) 071 { 072 mFin = fin; 073 074 return this; 075 } 076 077 078 /** 079 * Get the value of RSV1 bit. 080 * 081 * @return 082 * The value of RSV1 bit. 083 */ 084 public boolean getRsv1() 085 { 086 return mRsv1; 087 } 088 089 090 /** 091 * Set the value of RSV1 bit. 092 * 093 * @param rsv1 094 * The value of RSV1 bit. 095 * 096 * @return 097 * {@code this} object. 098 */ 099 public WebSocketFrame setRsv1(boolean rsv1) 100 { 101 mRsv1 = rsv1; 102 103 return this; 104 } 105 106 107 /** 108 * Get the value of RSV2 bit. 109 * 110 * @return 111 * The value of RSV2 bit. 112 */ 113 public boolean getRsv2() 114 { 115 return mRsv2; 116 } 117 118 119 /** 120 * Set the value of RSV2 bit. 121 * 122 * @param rsv2 123 * The value of RSV2 bit. 124 * 125 * @return 126 * {@code this} object. 127 */ 128 public WebSocketFrame setRsv2(boolean rsv2) 129 { 130 mRsv2 = rsv2; 131 132 return this; 133 } 134 135 136 /** 137 * Get the value of RSV3 bit. 138 * 139 * @return 140 * The value of RSV3 bit. 141 */ 142 public boolean getRsv3() 143 { 144 return mRsv3; 145 } 146 147 148 /** 149 * Set the value of RSV3 bit. 150 * 151 * @param rsv3 152 * The value of RSV3 bit. 153 * 154 * @return 155 * {@code this} object. 156 */ 157 public WebSocketFrame setRsv3(boolean rsv3) 158 { 159 mRsv3 = rsv3; 160 161 return this; 162 } 163 164 165 /** 166 * Get the opcode. 167 * 168 * <table border="1" cellpadding="5" style="table-collapse: collapse;"> 169 * <caption>WebSocket opcode</caption> 170 * <thead> 171 * <tr> 172 * <th>Value</th> 173 * <th>Description</th> 174 * </tr> 175 * </thead> 176 * <tbody> 177 * <tr> 178 * <td>0x0</td> 179 * <td>Frame continuation</td> 180 * </tr> 181 * <tr> 182 * <td>0x1</td> 183 * <td>Text frame</td> 184 * </tr> 185 * <tr> 186 * <td>0x2</td> 187 * <td>Binary frame</td> 188 * </tr> 189 * <tr> 190 * <td>0x3-0x7</td> 191 * <td>Reserved</td> 192 * </tr> 193 * <tr> 194 * <td>0x8</td> 195 * <td>Connection close</td> 196 * </tr> 197 * <tr> 198 * <td>0x9</td> 199 * <td>Ping</td> 200 * </tr> 201 * <tr> 202 * <td>0xA</td> 203 * <td>Pong</td> 204 * </tr> 205 * <tr> 206 * <td>0xB-0xF</td> 207 * <td>Reserved</td> 208 * </tr> 209 * </tbody> 210 * </table> 211 * 212 * @return 213 * The opcode. 214 * 215 * @see WebSocketOpcode 216 */ 217 public int getOpcode() 218 { 219 return mOpcode; 220 } 221 222 223 /** 224 * Set the opcode 225 * 226 * @param opcode 227 * The opcode. 228 * 229 * @return 230 * {@code this} object. 231 * 232 * @see WebSocketOpcode 233 */ 234 public WebSocketFrame setOpcode(int opcode) 235 { 236 mOpcode = opcode; 237 238 return this; 239 } 240 241 242 /** 243 * Check if this frame is a continuation frame. 244 * 245 * <p> 246 * This method returns {@code true} when the value of the 247 * opcode is 0x0 ({@link WebSocketOpcode#CONTINUATION}). 248 * </p> 249 * 250 * @return 251 * {@code true} if this frame is a continuation frame 252 * (= if the opcode is 0x0). 253 */ 254 public boolean isContinuationFrame() 255 { 256 return (mOpcode == CONTINUATION); 257 } 258 259 260 /** 261 * Check if this frame is a text frame. 262 * 263 * <p> 264 * This method returns {@code true} when the value of the 265 * opcode is 0x1 ({@link WebSocketOpcode#TEXT}). 266 * </p> 267 * 268 * @return 269 * {@code true} if this frame is a text frame 270 * (= if the opcode is 0x1). 271 */ 272 public boolean isTextFrame() 273 { 274 return (mOpcode == TEXT); 275 } 276 277 278 /** 279 * Check if this frame is a binary frame. 280 * 281 * <p> 282 * This method returns {@code true} when the value of the 283 * opcode is 0x2 ({@link WebSocketOpcode#BINARY}). 284 * </p> 285 * 286 * @return 287 * {@code true} if this frame is a binary frame 288 * (= if the opcode is 0x2). 289 */ 290 public boolean isBinaryFrame() 291 { 292 return (mOpcode == BINARY); 293 } 294 295 296 /** 297 * Check if this frame is a close frame. 298 * 299 * <p> 300 * This method returns {@code true} when the value of the 301 * opcode is 0x8 ({@link WebSocketOpcode#CLOSE}). 302 * </p> 303 * 304 * @return 305 * {@code true} if this frame is a close frame 306 * (= if the opcode is 0x8). 307 */ 308 public boolean isCloseFrame() 309 { 310 return (mOpcode == CLOSE); 311 } 312 313 314 /** 315 * Check if this frame is a ping frame. 316 * 317 * <p> 318 * This method returns {@code true} when the value of the 319 * opcode is 0x9 ({@link WebSocketOpcode#PING}). 320 * </p> 321 * 322 * @return 323 * {@code true} if this frame is a ping frame 324 * (= if the opcode is 0x9). 325 */ 326 public boolean isPingFrame() 327 { 328 return (mOpcode == PING); 329 } 330 331 332 /** 333 * Check if this frame is a pong frame. 334 * 335 * <p> 336 * This method returns {@code true} when the value of the 337 * opcode is 0xA ({@link WebSocketOpcode#PONG}). 338 * </p> 339 * 340 * @return 341 * {@code true} if this frame is a pong frame 342 * (= if the opcode is 0xA). 343 */ 344 public boolean isPongFrame() 345 { 346 return (mOpcode == PONG); 347 } 348 349 350 /** 351 * Check if this frame is a data frame. 352 * 353 * <p> 354 * This method returns {@code true} when the value of the 355 * opcode is in between 0x1 and 0x7. 356 * </p> 357 * 358 * @return 359 * {@code true} if this frame is a data frame 360 * (= if the opcode is in between 0x1 and 0x7). 361 */ 362 public boolean isDataFrame() 363 { 364 return (0x1 <= mOpcode && mOpcode <= 0x7); 365 } 366 367 368 /** 369 * Check if this frame is a control frame. 370 * 371 * <p> 372 * This method returns {@code true} when the value of the 373 * opcode is in between 0x8 and 0xF. 374 * </p> 375 * 376 * @return 377 * {@code true} if this frame is a control frame 378 * (= if the opcode is in between 0x8 and 0xF). 379 */ 380 public boolean isControlFrame() 381 { 382 return (0x8 <= mOpcode && mOpcode <= 0xF); 383 } 384 385 386 /** 387 * Get the value of MASK bit. 388 * 389 * @return 390 * The value of MASK bit. 391 */ 392 boolean getMask() 393 { 394 return mMask; 395 } 396 397 398 /** 399 * Set the value of MASK bit. 400 * 401 * @param mask 402 * The value of MASK bit. 403 * 404 * @return 405 * {@code this} object. 406 */ 407 WebSocketFrame setMask(boolean mask) 408 { 409 mMask = mask; 410 411 return this; 412 } 413 414 415 /** 416 * Check if this frame has payload. 417 * 418 * @return 419 * {@code true} if this frame has payload. 420 */ 421 public boolean hasPayload() 422 { 423 return mPayload != null; 424 } 425 426 427 /** 428 * Get the payload length. 429 * 430 * @return 431 * The payload length. 432 */ 433 public int getPayloadLength() 434 { 435 if (mPayload == null) 436 { 437 return 0; 438 } 439 440 return mPayload.length; 441 } 442 443 444 /** 445 * Get the unmasked payload. 446 * 447 * @return 448 * The unmasked payload. {@code null} may be returned. 449 */ 450 public byte[] getPayload() 451 { 452 return mPayload; 453 } 454 455 456 /** 457 * Get the unmasked payload as a text. 458 * 459 * @return 460 * A string constructed by interrupting the payload 461 * as a UTF-8 bytes. 462 */ 463 public String getPayloadText() 464 { 465 if (mPayload == null) 466 { 467 return null; 468 } 469 470 return Misc.toStringUTF8(mPayload); 471 } 472 473 474 /** 475 * Set the unmasked payload. 476 * 477 * <p> 478 * Note that the payload length of a <a href="http://tools.ietf.org/html/rfc6455#section-5.5" 479 * >control frame</a> must be 125 bytes or less. 480 * </p> 481 * 482 * @param payload 483 * The unmasked payload. {@code null} is accepted. 484 * An empty byte array is treated in the same way 485 * as {@code null}. 486 * 487 * @return 488 * {@code this} object. 489 */ 490 public WebSocketFrame setPayload(byte[] payload) 491 { 492 if (payload != null && payload.length == 0) 493 { 494 payload = null; 495 } 496 497 mPayload = payload; 498 499 return this; 500 } 501 502 503 /** 504 * Set the payload. The given string is converted to a byte array 505 * in UTF-8 encoding. 506 * 507 * <p> 508 * Note that the payload length of a <a href="http://tools.ietf.org/html/rfc6455#section-5.5" 509 * >control frame</a> must be 125 bytes or less. 510 * </p> 511 * 512 * @param payload 513 * The unmasked payload. {@code null} is accepted. 514 * An empty string is treated in the same way as 515 * {@code null}. 516 * 517 * @return 518 * {@code this} object. 519 */ 520 public WebSocketFrame setPayload(String payload) 521 { 522 if (payload == null || payload.length() == 0) 523 { 524 return setPayload((byte[])null); 525 } 526 527 return setPayload(Misc.getBytesUTF8(payload)); 528 } 529 530 531 /** 532 * Set the payload that conforms to the payload format of close frames. 533 * 534 * <p> 535 * The given parameters are encoded based on the rules described in 536 * "<a href="http://tools.ietf.org/html/rfc6455#section-5.5.1" 537 * >5.5.1. Close</a>" of RFC 6455. 538 * </p> 539 * 540 * <p> 541 * Note that the reason should not be too long because the payload 542 * length of a <a href="http://tools.ietf.org/html/rfc6455#section-5.5" 543 * >control frame</a> must be 125 bytes or less. 544 * </p> 545 * 546 * @param closeCode 547 * The close code. 548 * 549 * @param reason 550 * The reason. {@code null} is accepted. An empty string 551 * is treated in the same way as {@code null}. 552 * 553 * @return 554 * {@code this} object. 555 * 556 * @see <a href="http://tools.ietf.org/html/rfc6455#section-5.5.1" 557 * >RFC 6455, 5.5.1. Close</a> 558 * 559 * @see WebSocketCloseCode 560 */ 561 public WebSocketFrame setCloseFramePayload(int closeCode, String reason) 562 { 563 // Convert the close code to a 2-byte unsigned integer 564 // in network byte order. 565 byte[] encodedCloseCode = new byte[] { 566 (byte)((closeCode >> 8) & 0xFF), 567 (byte)((closeCode ) & 0xFF) 568 }; 569 570 // If a reason string is not given. 571 if (reason == null || reason.length() == 0) 572 { 573 // Use the close code only. 574 return setPayload(encodedCloseCode); 575 } 576 577 // Convert the reason into a byte array. 578 byte[] encodedReason = Misc.getBytesUTF8(reason); 579 580 // Concatenate the close code and the reason. 581 byte[] payload = new byte[2 + encodedReason.length]; 582 System.arraycopy(encodedCloseCode, 0, payload, 0, 2); 583 System.arraycopy(encodedReason, 0, payload, 2, encodedReason.length); 584 585 // Use the concatenated string. 586 return setPayload(payload); 587 } 588 589 590 /** 591 * Parse the first two bytes of the payload as a close code. 592 * 593 * <p> 594 * If any payload is not set or the length of the payload is less than 2, 595 * this method returns 1005 ({@link WebSocketCloseCode#NONE}). 596 * </p> 597 * 598 * <p> 599 * The value returned from this method is meaningless if this frame 600 * is not a close frame. 601 * </p> 602 * 603 * @return 604 * The close code. 605 * 606 * @see <a href="http://tools.ietf.org/html/rfc6455#section-5.5.1" 607 * >RFC 6455, 5.5.1. Close</a> 608 * 609 * @see WebSocketCloseCode 610 */ 611 public int getCloseCode() 612 { 613 if (mPayload == null || mPayload.length < 2) 614 { 615 return WebSocketCloseCode.NONE; 616 } 617 618 // A close code is encoded in network byte order. 619 int closeCode = (((mPayload[0] & 0xFF) << 8) | (mPayload[1] & 0xFF)); 620 621 return closeCode; 622 } 623 624 625 /** 626 * Parse the third and subsequent bytes of the payload as a close reason. 627 * 628 * <p> 629 * If any payload is not set or the length of the payload is less than 3, 630 * this method returns {@code null}. 631 * </p> 632 * 633 * <p> 634 * The value returned from this method is meaningless if this frame 635 * is not a close frame. 636 * </p> 637 * 638 * @return 639 * The close reason. 640 */ 641 public String getCloseReason() 642 { 643 if (mPayload == null || mPayload.length < 3) 644 { 645 return null; 646 } 647 648 return Misc.toStringUTF8(mPayload, 2, mPayload.length - 2); 649 } 650 651 652 @Override 653 public String toString() 654 { 655 StringBuilder builder = new StringBuilder() 656 .append("WebSocketFrame(FIN=").append(mFin ? "1" : "0") 657 .append(",RSV1=").append(mRsv1 ? "1" : "0") 658 .append(",RSV2=").append(mRsv2 ? "1" : "0") 659 .append(",RSV3=").append(mRsv3 ? "1" : "0") 660 .append(",Opcode=").append(Misc.toOpcodeName(mOpcode)) 661 .append(",Length=").append(getPayloadLength()); 662 663 switch (mOpcode) 664 { 665 case TEXT: 666 appendPayloadText(builder); 667 break; 668 669 case BINARY: 670 appendPayloadBinary(builder); 671 break; 672 673 case CLOSE: 674 appendPayloadClose(builder); 675 break; 676 } 677 678 return builder.append(")").toString(); 679 } 680 681 682 private boolean appendPayloadCommon(StringBuilder builder) 683 { 684 builder.append(",Payload="); 685 686 if (mPayload == null) 687 { 688 builder.append("null"); 689 690 // Nothing more to append. 691 return true; 692 } 693 694 if (mRsv1) 695 { 696 // In the current implementation, mRsv1=true is allowed 697 // only when Per-Message Compression is applied. 698 builder.append("compressed"); 699 700 // Nothing more to append. 701 return true; 702 } 703 704 // Continue. 705 return false; 706 } 707 708 709 private void appendPayloadText(StringBuilder builder) 710 { 711 if (appendPayloadCommon(builder)) 712 { 713 // Nothing more to append. 714 return; 715 } 716 717 builder.append("\""); 718 builder.append(getPayloadText()); 719 builder.append("\""); 720 } 721 722 723 private void appendPayloadClose(StringBuilder builder) 724 { 725 builder 726 .append(",CloseCode=").append(getCloseCode()) 727 .append(",Reason="); 728 729 String reason = getCloseReason(); 730 731 if (reason == null) 732 { 733 builder.append("null"); 734 } 735 else 736 { 737 builder.append("\"").append(reason).append("\""); 738 } 739 } 740 741 742 private void appendPayloadBinary(StringBuilder builder) 743 { 744 if (appendPayloadCommon(builder)) 745 { 746 // Nothing more to append. 747 return; 748 } 749 750 for (int i = 0; i < mPayload.length; ++i) 751 { 752 builder.append(String.format("%02X ", (0xFF & mPayload[i]))); 753 } 754 755 if (mPayload.length != 0) 756 { 757 // Remove the last space. 758 builder.setLength(builder.length() - 1); 759 } 760 } 761 762 763 /** 764 * Create a continuation frame. Note that the FIN bit of the 765 * returned frame is false. 766 * 767 * @return 768 * A WebSocket frame whose FIN bit is false, opcode is 769 * {@link WebSocketOpcode#CONTINUATION CONTINUATION} and 770 * payload is {@code null}. 771 */ 772 public static WebSocketFrame createContinuationFrame() 773 { 774 return new WebSocketFrame() 775 .setOpcode(CONTINUATION); 776 } 777 778 779 /** 780 * Create a continuation frame. Note that the FIN bit of the 781 * returned frame is false. 782 * 783 * @param payload 784 * The payload for a newly create frame. 785 * 786 * @return 787 * A WebSocket frame whose FIN bit is false, opcode is 788 * {@link WebSocketOpcode#CONTINUATION CONTINUATION} and 789 * payload is the given one. 790 */ 791 public static WebSocketFrame createContinuationFrame(byte[] payload) 792 { 793 return createContinuationFrame().setPayload(payload); 794 } 795 796 797 /** 798 * Create a continuation frame. Note that the FIN bit of the 799 * returned frame is false. 800 * 801 * @param payload 802 * The payload for a newly create frame. 803 * 804 * @return 805 * A WebSocket frame whose FIN bit is false, opcode is 806 * {@link WebSocketOpcode#CONTINUATION CONTINUATION} and 807 * payload is the given one. 808 */ 809 public static WebSocketFrame createContinuationFrame(String payload) 810 { 811 return createContinuationFrame().setPayload(payload); 812 } 813 814 815 /** 816 * Create a text frame. 817 * 818 * @param payload 819 * The payload for a newly created frame. 820 * 821 * @return 822 * A WebSocket frame whose FIN bit is true, opcode is 823 * {@link WebSocketOpcode#TEXT TEXT} and payload is 824 * the given one. 825 */ 826 public static WebSocketFrame createTextFrame(String payload) 827 { 828 return new WebSocketFrame() 829 .setFin(true) 830 .setOpcode(TEXT) 831 .setPayload(payload); 832 } 833 834 835 /** 836 * Create a binary frame. 837 * 838 * @param payload 839 * The payload for a newly created frame. 840 * 841 * @return 842 * A WebSocket frame whose FIN bit is true, opcode is 843 * {@link WebSocketOpcode#BINARY BINARY} and payload is 844 * the given one. 845 */ 846 public static WebSocketFrame createBinaryFrame(byte[] payload) 847 { 848 return new WebSocketFrame() 849 .setFin(true) 850 .setOpcode(BINARY) 851 .setPayload(payload); 852 } 853 854 855 /** 856 * Create a close frame. 857 * 858 * @return 859 * A WebSocket frame whose FIN bit is true, opcode is 860 * {@link WebSocketOpcode#CLOSE CLOSE} and payload is 861 * {@code null}. 862 */ 863 public static WebSocketFrame createCloseFrame() 864 { 865 return new WebSocketFrame() 866 .setFin(true) 867 .setOpcode(CLOSE); 868 } 869 870 871 /** 872 * Create a close frame. 873 * 874 * @param closeCode 875 * The close code. 876 * 877 * @return 878 * A WebSocket frame whose FIN bit is true, opcode is 879 * {@link WebSocketOpcode#CLOSE CLOSE} and payload 880 * contains a close code. 881 * 882 * @see WebSocketCloseCode 883 */ 884 public static WebSocketFrame createCloseFrame(int closeCode) 885 { 886 return createCloseFrame().setCloseFramePayload(closeCode, null); 887 } 888 889 890 /** 891 * Create a close frame. 892 * 893 * @param closeCode 894 * The close code. 895 * 896 * @param reason 897 * The close reason. 898 * Note that a control frame's payload length must be 125 bytes or less 899 * (RFC 6455, <a href="https://tools.ietf.org/html/rfc6455#section-5.5" 900 * >5.5. Control Frames</a>). 901 * 902 * @return 903 * A WebSocket frame whose FIN bit is true, opcode is 904 * {@link WebSocketOpcode#CLOSE CLOSE} and payload 905 * contains a close code and a close reason. 906 * 907 * @see WebSocketCloseCode 908 */ 909 public static WebSocketFrame createCloseFrame(int closeCode, String reason) 910 { 911 return createCloseFrame().setCloseFramePayload(closeCode, reason); 912 } 913 914 915 /** 916 * Create a ping frame. 917 * 918 * @return 919 * A WebSocket frame whose FIN bit is true, opcode is 920 * {@link WebSocketOpcode#PING PING} and payload is 921 * {@code null}. 922 */ 923 public static WebSocketFrame createPingFrame() 924 { 925 return new WebSocketFrame() 926 .setFin(true) 927 .setOpcode(PING); 928 } 929 930 931 /** 932 * Create a ping frame. 933 * 934 * @param payload 935 * The payload for a newly created frame. 936 * Note that a control frame's payload length must be 125 bytes or less 937 * (RFC 6455, <a href="https://tools.ietf.org/html/rfc6455#section-5.5" 938 * >5.5. Control Frames</a>). 939 * 940 * @return 941 * A WebSocket frame whose FIN bit is true, opcode is 942 * {@link WebSocketOpcode#PING PING} and payload is 943 * the given one. 944 */ 945 public static WebSocketFrame createPingFrame(byte[] payload) 946 { 947 return createPingFrame().setPayload(payload); 948 } 949 950 951 /** 952 * Create a ping frame. 953 * 954 * @param payload 955 * The payload for a newly created frame. 956 * Note that a control frame's payload length must be 125 bytes or less 957 * (RFC 6455, <a href="https://tools.ietf.org/html/rfc6455#section-5.5" 958 * >5.5. Control Frames</a>). 959 * 960 * @return 961 * A WebSocket frame whose FIN bit is true, opcode is 962 * {@link WebSocketOpcode#PING PING} and payload is 963 * the given one. 964 */ 965 public static WebSocketFrame createPingFrame(String payload) 966 { 967 return createPingFrame().setPayload(payload); 968 } 969 970 971 /** 972 * Create a pong frame. 973 * 974 * @return 975 * A WebSocket frame whose FIN bit is true, opcode is 976 * {@link WebSocketOpcode#PONG PONG} and payload is 977 * {@code null}. 978 */ 979 public static WebSocketFrame createPongFrame() 980 { 981 return new WebSocketFrame() 982 .setFin(true) 983 .setOpcode(PONG); 984 } 985 986 987 /** 988 * Create a pong frame. 989 * 990 * @param payload 991 * The payload for a newly created frame. 992 * Note that a control frame's payload length must be 125 bytes or less 993 * (RFC 6455, <a href="https://tools.ietf.org/html/rfc6455#section-5.5" 994 * >5.5. Control Frames</a>). 995 * 996 * @return 997 * A WebSocket frame whose FIN bit is true, opcode is 998 * {@link WebSocketOpcode#PONG PONG} and payload is 999 * the given one. 1000 */ 1001 public static WebSocketFrame createPongFrame(byte[] payload) 1002 { 1003 return createPongFrame().setPayload(payload); 1004 } 1005 1006 1007 /** 1008 * Create a pong frame. 1009 * 1010 * @param payload 1011 * The payload for a newly created frame. 1012 * Note that a control frame's payload length must be 125 bytes or less 1013 * (RFC 6455, <a href="https://tools.ietf.org/html/rfc6455#section-5.5" 1014 * >5.5. Control Frames</a>). 1015 * 1016 * @return 1017 * A WebSocket frame whose FIN bit is true, opcode is 1018 * {@link WebSocketOpcode#PONG PONG} and payload is 1019 * the given one. 1020 */ 1021 public static WebSocketFrame createPongFrame(String payload) 1022 { 1023 return createPongFrame().setPayload(payload); 1024 } 1025 1026 1027 /** 1028 * Mask/unmask payload. 1029 * 1030 * <p> 1031 * The logic of masking/unmasking is described in "<a href= 1032 * "http://tools.ietf.org/html/rfc6455#section-5.3">5.3. 1033 * Client-to-Server Masking</a>" in RFC 6455. 1034 * </p> 1035 * 1036 * @param maskingKey 1037 * The masking key. If {@code null} is given or the length 1038 * of the masking key is less than 4, nothing is performed. 1039 * 1040 * @param payload 1041 * Payload to be masked/unmasked. 1042 * 1043 * @return 1044 * {@code payload}. 1045 * 1046 * @see <a href="http://tools.ietf.org/html/rfc6455#section-5.3">5.3. Client-to-Server Masking</a> 1047 */ 1048 static byte[] mask(byte[] maskingKey, byte[] payload) 1049 { 1050 if (maskingKey == null || maskingKey.length < 4 || payload == null) 1051 { 1052 return payload; 1053 } 1054 1055 for (int i = 0; i < payload.length; ++i) 1056 { 1057 payload[i] ^= maskingKey[i % 4]; 1058 } 1059 1060 return payload; 1061 } 1062 1063 1064 static WebSocketFrame compressFrame(WebSocketFrame frame, PerMessageCompressionExtension pmce) 1065 { 1066 // If Per-Message Compression is not enabled. 1067 if (pmce == null) 1068 { 1069 // No compression. 1070 return frame; 1071 } 1072 1073 // If the frame is neither a TEXT frame nor a BINARY frame. 1074 if (frame.isTextFrame() == false && 1075 frame.isBinaryFrame() == false) 1076 { 1077 // No compression. 1078 return frame; 1079 } 1080 1081 // If the frame is not the final frame. 1082 if (frame.getFin() == false) 1083 { 1084 // The compression must be applied to this frame and 1085 // all the subsequent continuation frames, but the 1086 // current implementation does not support the behavior. 1087 return frame; 1088 } 1089 1090 // If the RSV1 bit is set. 1091 if (frame.getRsv1()) 1092 { 1093 // In the current implementation, RSV1=true is allowed 1094 // only as Per-Message Compressed Bit (See RFC 7692, 1095 // 6. Framing). Therefore, RSV1=true here is regarded 1096 // as "already compressed". 1097 return frame; 1098 } 1099 1100 // The plain payload before compression. 1101 byte[] payload = frame.getPayload(); 1102 1103 // If the payload is empty. 1104 if (payload == null || payload.length == 0) 1105 { 1106 // No compression. 1107 return frame; 1108 } 1109 1110 // Compress the payload. 1111 byte[] compressed = compress(payload, pmce); 1112 1113 // If the length of the compressed data is not less than 1114 // that of the original plain payload. 1115 if (payload.length <= compressed.length) 1116 { 1117 // It's better not to compress the payload. 1118 return frame; 1119 } 1120 1121 // Replace the plain payload with the compressed data. 1122 frame.setPayload(compressed); 1123 1124 // Set Per-Message Compressed Bit (See RFC 7692, 6. Framing). 1125 frame.setRsv1(true); 1126 1127 return frame; 1128 } 1129 1130 1131 private static byte[] compress(byte[] data, PerMessageCompressionExtension pmce) 1132 { 1133 try 1134 { 1135 // Compress the data. 1136 return pmce.compress(data); 1137 } 1138 catch (WebSocketException e) 1139 { 1140 // Failed to compress the data. Ignore this error and use 1141 // the plain original data. The current implementation 1142 // does not call any listener callback method for this error. 1143 return data; 1144 } 1145 } 1146 1147 1148 static List<WebSocketFrame> splitIfNecessary( 1149 WebSocketFrame frame, int maxPayloadSize, PerMessageCompressionExtension pmce) 1150 { 1151 // If the maximum payload size is not specified. 1152 if (maxPayloadSize == 0) 1153 { 1154 // Not split. 1155 return null; 1156 } 1157 1158 // If the total length of the payload is equal to or 1159 // less than the maximum payload size. 1160 if (frame.getPayloadLength() <= maxPayloadSize) 1161 { 1162 // Not split. 1163 return null; 1164 } 1165 1166 // If the frame is a binary frame or a text frame. 1167 if (frame.isBinaryFrame() || frame.isTextFrame()) 1168 { 1169 // Try to compress the frame. In the current implementation, binary 1170 // frames and text frames with the FIN bit true can be compressed. 1171 // The compressFrame() method may change the payload and the RSV1 1172 // bit of the given frame. 1173 frame = compressFrame(frame, pmce); 1174 1175 // If the payload length of the frame has become equal to or less 1176 // than the maximum payload size as a result of the compression. 1177 if (frame.getPayloadLength() <= maxPayloadSize) 1178 { 1179 // Not split. (Note that the frame has been compressed) 1180 return null; 1181 } 1182 } 1183 else if (frame.isContinuationFrame() == false) 1184 { 1185 // Control frames (Close/Ping/Pong) are not split. 1186 return null; 1187 } 1188 1189 // Split the frame. 1190 return split(frame, maxPayloadSize); 1191 } 1192 1193 1194 private static List<WebSocketFrame> split(WebSocketFrame frame, int maxPayloadSize) 1195 { 1196 // The original payload and the original FIN bit. 1197 byte[] originalPayload = frame.getPayload(); 1198 boolean originalFin = frame.getFin(); 1199 1200 List<WebSocketFrame> frames = new ArrayList<WebSocketFrame>(); 1201 1202 // Generate the first frame using the existing WebSocketFrame instance. 1203 // Note that the reserved bit 1 and the opcode are untouched. 1204 byte[] payload = Arrays.copyOf(originalPayload, maxPayloadSize); 1205 frame.setFin(false).setPayload(payload); 1206 frames.add(frame); 1207 1208 for (int from = maxPayloadSize; from < originalPayload.length; from += maxPayloadSize) 1209 { 1210 // Prepare the payload of the next continuation frame. 1211 int to = Math.min(from + maxPayloadSize, originalPayload.length); 1212 payload = Arrays.copyOfRange(originalPayload, from, to); 1213 1214 // Create a continuation frame. 1215 WebSocketFrame cont = WebSocketFrame.createContinuationFrame(payload); 1216 frames.add(cont); 1217 } 1218 1219 if (originalFin) 1220 { 1221 // Set the FIN bit of the last frame. 1222 frames.get(frames.size() - 1).setFin(true); 1223 } 1224 1225 return frames; 1226 } 1227}