66import org .java_websocket .extensions .CompressionExtension ;
77import org .java_websocket .extensions .ExtensionRequestData ;
88import org .java_websocket .extensions .IExtension ;
9- import org .java_websocket .framing .*;
9+ import org .java_websocket .framing .BinaryFrame ;
10+ import org .java_websocket .framing .CloseFrame ;
11+ import org .java_websocket .framing .ContinuousFrame ;
12+ import org .java_websocket .framing .DataFrame ;
13+ import org .java_websocket .framing .Framedata ;
14+ import org .java_websocket .framing .FramedataImpl1 ;
15+ import org .java_websocket .framing .TextFrame ;
1016
1117import java .io .ByteArrayOutputStream ;
1218import java .nio .ByteBuffer ;
1622import java .util .zip .Deflater ;
1723import java .util .zip .Inflater ;
1824
25+ /**
26+ * PerMessage Deflate Extension (<a href="https://tools.ietf.org/html/rfc7692#section-7">7. The "permessage-deflate" Extension</a> in
27+ * <a href="https://tools.ietf.org/html/rfc7692">RFC 7692</a>).
28+ *
29+ * @see <a href="https://tools.ietf.org/html/rfc7692#section-7">7. The "permessage-deflate" Extension in RFC 7692</a>
30+ */
1931public class PerMessageDeflateExtension extends CompressionExtension {
2032
2133 // Name of the extension as registered by IETF https://tools.ietf.org/html/rfc7692#section-9.
@@ -28,7 +40,7 @@ public class PerMessageDeflateExtension extends CompressionExtension {
2840 private static final String CLIENT_MAX_WINDOW_BITS = "client_max_window_bits" ;
2941 private static final int serverMaxWindowBits = 1 << 15 ;
3042 private static final int clientMaxWindowBits = 1 << 15 ;
31- private static final byte [] TAIL_BYTES = {0x00 , 0x00 , (byte )0xFF , (byte )0xFF };
43+ private static final byte [] TAIL_BYTES = { ( byte ) 0x00 , ( byte ) 0x00 , (byte )0xFF , (byte )0xFF };
3244 private static final int BUFFER_SIZE = 1 << 10 ;
3345
3446 private boolean serverNoContextTakeover = true ;
@@ -37,9 +49,60 @@ public class PerMessageDeflateExtension extends CompressionExtension {
3749 // For WebSocketServers, this variable holds the extension parameters that the peer client has requested.
3850 // For WebSocketClients, this variable holds the extension parameters that client himself has requested.
3951 private Map <String , String > requestedParameters = new LinkedHashMap <String , String >();
52+
4053 private Inflater inflater = new Inflater (true );
4154 private Deflater deflater = new Deflater (Deflater .DEFAULT_COMPRESSION , true );
4255
56+ public Inflater getInflater () {
57+ return inflater ;
58+ }
59+
60+ public void setInflater (Inflater inflater ) {
61+ this .inflater = inflater ;
62+ }
63+
64+ public Deflater getDeflater () {
65+ return deflater ;
66+ }
67+
68+ public void setDeflater (Deflater deflater ) {
69+ this .deflater = deflater ;
70+ }
71+
72+ /**
73+ *
74+ * @return serverNoContextTakeover
75+ */
76+ public boolean isServerNoContextTakeover ()
77+ {
78+ return serverNoContextTakeover ;
79+ }
80+
81+ /**
82+ *
83+ * @param serverNoContextTakeover
84+ */
85+ public void setServerNoContextTakeover (boolean serverNoContextTakeover ) {
86+ this .serverNoContextTakeover = serverNoContextTakeover ;
87+ }
88+
89+ /**
90+ *
91+ * @return clientNoContextTakeover
92+ */
93+ public boolean isClientNoContextTakeover ()
94+ {
95+ return clientNoContextTakeover ;
96+ }
97+
98+ /**
99+ *
100+ * @param clientNoContextTakeover
101+ */
102+ public void setClientNoContextTakeover (boolean clientNoContextTakeover ) {
103+ this .clientNoContextTakeover = clientNoContextTakeover ;
104+ }
105+
43106 /*
44107 An endpoint uses the following algorithm to decompress a message.
45108 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
@@ -50,11 +113,11 @@ public class PerMessageDeflateExtension extends CompressionExtension {
50113 @ Override
51114 public void decodeFrame (Framedata inputFrame ) throws InvalidDataException {
52115 // Only DataFrames can be decompressed.
53- if (!(inputFrame instanceof DataFrame ))
116+ if (!(inputFrame instanceof DataFrame ))
54117 return ;
55118
56119 // RSV1 bit must be set only for the first frame.
57- if (inputFrame .getOpcode () == Opcode .CONTINUOUS && inputFrame .isRSV1 ())
120+ if (inputFrame .getOpcode () == Opcode .CONTINUOUS && inputFrame .isRSV1 ())
58121 throw new InvalidDataException (CloseFrame .POLICY_VALIDATION , "RSV1 bit can only be set for the first frame." );
59122
60123 // Decompressed output buffer.
@@ -70,47 +133,53 @@ We can check the getRemaining() method to see whether the data we supplied has b
70133 And if not, we just reset the inflater and decompress again.
71134 Note that this behavior doesn't occur if the message is "first compressed and then fragmented".
72135 */
73- if (inflater .getRemaining () > 0 ){
136+ if (inflater .getRemaining () > 0 ) {
74137 inflater = new Inflater (true );
75138 decompress (inputFrame .getPayloadData ().array (), output );
76139 }
77140
78- if (inputFrame .isFin ()) {
141+ if (inputFrame .isFin ()) {
79142 decompress (TAIL_BYTES , output );
80143 // If context takeover is disabled, inflater can be reset.
81- if (clientNoContextTakeover )
144+ if (clientNoContextTakeover )
82145 inflater = new Inflater (true );
83146 }
84147 } catch (DataFormatException e ) {
85148 throw new InvalidDataException (CloseFrame .POLICY_VALIDATION , e .getMessage ());
86149 }
87150
88151 // RSV1 bit must be cleared after decoding, so that other extensions don't throw an exception.
89- if (inputFrame .isRSV1 ())
152+ if (inputFrame .isRSV1 ())
90153 ((DataFrame ) inputFrame ).setRSV1 (false );
91154
92155 // Set frames payload to the new decompressed data.
93156 ((FramedataImpl1 ) inputFrame ).setPayload (ByteBuffer .wrap (output .toByteArray (), 0 , output .size ()));
94157 }
95158
96- private void decompress (byte [] data , ByteArrayOutputStream outputBuffer ) throws DataFormatException {
159+ /**
160+ *
161+ * @param data the bytes of data
162+ * @param outputBuffer the output stream
163+ * @throws DataFormatException
164+ */
165+ private void decompress (byte [] data , ByteArrayOutputStream outputBuffer ) throws DataFormatException {
97166 inflater .setInput (data );
98167 byte [] buffer = new byte [BUFFER_SIZE ];
99168
100169 int bytesInflated ;
101- while ((bytesInflated = inflater .inflate (buffer )) > 0 ){
170+ while ((bytesInflated = inflater .inflate (buffer )) > 0 ) {
102171 outputBuffer .write (buffer , 0 , bytesInflated );
103172 }
104173 }
105174
106175 @ Override
107176 public void encodeFrame (Framedata inputFrame ) {
108177 // Only DataFrames can be decompressed.
109- if (!(inputFrame instanceof DataFrame ))
178+ if (!(inputFrame instanceof DataFrame ))
110179 return ;
111180
112181 // Only the first frame's RSV1 must be set.
113- if (!(inputFrame instanceof ContinuousFrame ))
182+ if (!(inputFrame instanceof ContinuousFrame ))
114183 ((DataFrame ) inputFrame ).setRSV1 (true );
115184
116185 deflater .setInput (inputFrame .getPayloadData ().array ());
@@ -119,7 +188,7 @@ public void encodeFrame(Framedata inputFrame) {
119188 // Temporary buffer to hold compressed output.
120189 byte [] buffer = new byte [1024 ];
121190 int bytesCompressed ;
122- while ((bytesCompressed = deflater .deflate (buffer , 0 , buffer .length , Deflater .SYNC_FLUSH )) > 0 ) {
191+ while ((bytesCompressed = deflater .deflate (buffer , 0 , buffer .length , Deflater .SYNC_FLUSH )) > 0 ) {
123192 output .write (buffer , 0 , bytesCompressed );
124193 }
125194
@@ -132,11 +201,11 @@ public void encodeFrame(Framedata inputFrame) {
132201 To simulate removal, we just pass 4 bytes less to the new payload
133202 if the frame is final and outputBytes ends with 0x00 0x00 0xff 0xff.
134203 */
135- if (inputFrame .isFin ()) {
136- if (endsWithTail (outputBytes ))
204+ if (inputFrame .isFin ()) {
205+ if (endsWithTail (outputBytes ))
137206 outputLength -= TAIL_BYTES .length ;
138207
139- if (serverNoContextTakeover ) {
208+ if (serverNoContextTakeover ) {
140209 deflater .end ();
141210 deflater = new Deflater (Deflater .DEFAULT_COMPRESSION , true );
142211 }
@@ -146,13 +215,18 @@ public void encodeFrame(Framedata inputFrame) {
146215 ((FramedataImpl1 ) inputFrame ).setPayload (ByteBuffer .wrap (outputBytes , 0 , outputLength ));
147216 }
148217
149- private boolean endsWithTail (byte [] data ){
150- if (data .length < 4 )
218+ /**
219+ *
220+ * @param data the bytes of data
221+ * @return true if the data is OK
222+ */
223+ private boolean endsWithTail (byte [] data ) {
224+ if (data .length < 4 )
151225 return false ;
152226
153227 int length = data .length ;
154- for (int i = 0 ; i < TAIL_BYTES .length ; i ++){
155- if (TAIL_BYTES [i ] != data [length - TAIL_BYTES .length + i ])
228+ for (int i = 0 ; i < TAIL_BYTES .length ; i ++) {
229+ if (TAIL_BYTES [i ] != data [length - TAIL_BYTES .length + i ])
156230 return false ;
157231 }
158232
@@ -162,15 +236,15 @@ private boolean endsWithTail(byte[] data){
162236 @ Override
163237 public boolean acceptProvidedExtensionAsServer (String inputExtension ) {
164238 String [] requestedExtensions = inputExtension .split ("," );
165- for (String extension : requestedExtensions ) {
239+ for (String extension : requestedExtensions ) {
166240 ExtensionRequestData extensionData = ExtensionRequestData .parseExtensionRequest (extension );
167- if (!EXTENSION_REGISTERED_NAME .equalsIgnoreCase (extensionData .getExtensionName ()))
241+ if (!EXTENSION_REGISTERED_NAME .equalsIgnoreCase (extensionData .getExtensionName ()))
168242 continue ;
169243
170244 // Holds parameters that peer client has sent.
171245 Map <String , String > headers = extensionData .getExtensionParameters ();
172246 requestedParameters .putAll (headers );
173- if (requestedParameters .containsKey (CLIENT_NO_CONTEXT_TAKEOVER ))
247+ if (requestedParameters .containsKey (CLIENT_NO_CONTEXT_TAKEOVER ))
174248 clientNoContextTakeover = true ;
175249
176250 return true ;
@@ -182,9 +256,9 @@ public boolean acceptProvidedExtensionAsServer(String inputExtension) {
182256 @ Override
183257 public boolean acceptProvidedExtensionAsClient (String inputExtension ) {
184258 String [] requestedExtensions = inputExtension .split ("," );
185- for (String extension : requestedExtensions ) {
259+ for (String extension : requestedExtensions ) {
186260 ExtensionRequestData extensionData = ExtensionRequestData .parseExtensionRequest (extension );
187- if (!EXTENSION_REGISTERED_NAME .equalsIgnoreCase (extensionData .getExtensionName ()))
261+ if (!EXTENSION_REGISTERED_NAME .equalsIgnoreCase (extensionData .getExtensionName ()))
188262 continue ;
189263
190264 // Holds parameters that are sent by the server, as a response to our initial extension request.
@@ -222,9 +296,9 @@ public IExtension copyInstance() {
222296 */
223297 @ Override
224298 public void isFrameValid (Framedata inputFrame ) throws InvalidDataException {
225- if ((inputFrame instanceof TextFrame || inputFrame instanceof BinaryFrame ) && !inputFrame .isRSV1 ())
299+ if ((inputFrame instanceof TextFrame || inputFrame instanceof BinaryFrame ) && !inputFrame .isRSV1 ())
226300 throw new InvalidFrameException ("RSV1 bit must be set for DataFrames." );
227- if ((inputFrame instanceof ContinuousFrame ) && (inputFrame .isRSV1 () || inputFrame .isRSV2 () || inputFrame .isRSV3 ()))
301+ if ((inputFrame instanceof ContinuousFrame ) && (inputFrame .isRSV1 () || inputFrame .isRSV2 () || inputFrame .isRSV3 ()))
228302 throw new InvalidFrameException ( "bad rsv RSV1: " + inputFrame .isRSV1 () + " RSV2: " + inputFrame .isRSV2 () + " RSV3: " + inputFrame .isRSV3 () );
229303 super .isFrameValid (inputFrame );
230304 }
0 commit comments