View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.websocket.common.extensions.compress;
20  
21  
22  import java.io.IOException;
23  import java.nio.ByteBuffer;
24  import java.util.concurrent.Future;
25  
26  import javax.net.websocket.SendResult;
27  
28  import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
29  import org.eclipse.jetty.websocket.api.extensions.Frame;
30  import org.eclipse.jetty.websocket.common.WebSocketFrame;
31  import org.eclipse.jetty.websocket.common.extensions.AbstractExtension;
32  
33  /**
34   * Implementation of the <a href="https://tools.ietf.org/id/draft-tyoshino-hybi-websocket-perframe-deflate-05.txt">x-webkit-deflate-frame</a> extension seen out
35   * in the wild.
36   */
37  public class FrameCompressionExtension extends AbstractExtension
38  {
39      private DeflateCompressionMethod method;
40  
41      @Override
42      public void incomingFrame(Frame frame)
43      {
44          if (frame.getType().isControl() || !frame.isRsv1())
45          {
46              // Cannot modify incoming control frames or ones with RSV1 set.
47              nextIncomingFrame(frame);
48              return;
49          }
50  
51          ByteBuffer data = frame.getPayload();
52          try
53          {
54              method.decompress().input(data);
55              while (!method.decompress().isDone())
56              {
57                  ByteBuffer uncompressed = method.decompress().process();
58                  WebSocketFrame out = new WebSocketFrame(frame).setPayload(uncompressed);
59                  if (!method.decompress().isDone())
60                  {
61                      out.setFin(false);
62                  }
63                  out.setRsv1(false); // Unset RSV1 on decompressed frame
64                  nextIncomingFrame(out);
65              }
66  
67              // reset on every frame.
68              // method.decompress().end();
69          }
70          finally
71          {
72              // release original buffer (no longer needed)
73              getBufferPool().release(data);
74          }
75      }
76  
77      /**
78       * Indicates use of RSV1 flag for indicating deflation is in use.
79       * <p>
80       * Also known as the "COMP" framing header bit
81       */
82      @Override
83      public boolean isRsv1User()
84      {
85          return true;
86      }
87  
88      /**
89       * Indicate that this extensions is now responsible for TEXT Data Frame compliance to the WebSocket spec.
90       */
91      @Override
92      public boolean isTextDataDecoder()
93      {
94          return true;
95      }
96  
97      @Override
98      public Future<SendResult> outgoingFrame(Frame frame) throws IOException
99      {
100         if (frame.getType().isControl())
101         {
102             // skip, cannot compress control frames.
103             return nextOutgoingFrame(frame);
104         }
105 
106         Future<SendResult> future = null;
107 
108         ByteBuffer data = frame.getPayload();
109         try
110         {
111             // deflate data
112             method.compress().input(data);
113             while (!method.compress().isDone())
114             {
115                 ByteBuffer buf = method.compress().process();
116                 WebSocketFrame out = new WebSocketFrame(frame).setPayload(buf);
117                 out.setRsv1(true);
118                 if (!method.compress().isDone())
119                 {
120                     out.setFin(false);
121                 }
122 
123                 future = nextOutgoingFrame(out);
124             }
125 
126             // reset on every frame.
127             method.compress().end();
128         }
129         finally
130         {
131             // free original data buffer
132             getBufferPool().release(data);
133         }
134 
135         return future;
136     }
137 
138     @Override
139     public void setConfig(ExtensionConfig config)
140     {
141         super.setConfig(config);
142 
143         method = new DeflateCompressionMethod();
144     }
145 
146     @Override
147     public String toString()
148     {
149         return this.getClass().getSimpleName() + "[]";
150     }
151 }