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}