View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.nio.ByteBuffer;
23  
24  import org.eclipse.jetty.websocket.api.WriteCallback;
25  import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
26  import org.eclipse.jetty.websocket.api.extensions.Frame;
27  import org.eclipse.jetty.websocket.common.WebSocketFrame;
28  import org.eclipse.jetty.websocket.common.extensions.AbstractExtension;
29  
30  /**
31   * 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
32   * in the wild.
33   */
34  public class FrameCompressionExtension extends AbstractExtension
35  {
36      private CompressionMethod method = new DeflateCompressionMethod();
37  
38      @Override
39      public synchronized void incomingFrame(Frame frame)
40      {
41          if (frame.getType().isControl() || !frame.isRsv1())
42          {
43              // Cannot modify incoming control frames or ones with RSV1 set.
44              nextIncomingFrame(frame);
45              return;
46          }
47  
48          ByteBuffer data = frame.getPayload();
49          method.decompress().input(data);
50          while (!method.decompress().isDone())
51          {
52              ByteBuffer uncompressed = method.decompress().process();
53              WebSocketFrame out = new WebSocketFrame(frame).setPayload(uncompressed);
54              if (!method.decompress().isDone())
55              {
56                  out.setFin(false);
57              }
58              out.setRsv1(false); // Unset RSV1 on decompressed frame
59              nextIncomingFrame(out);
60          }
61  
62          // reset on every frame.
63          // method.decompress().end();
64      }
65  
66      /**
67       * Indicates use of RSV1 flag for indicating deflation is in use.
68       * <p>
69       * Also known as the "COMP" framing header bit
70       */
71      @Override
72      public boolean isRsv1User()
73      {
74          return true;
75      }
76  
77      /**
78       * Indicate that this extensions is now responsible for TEXT Data Frame compliance to the WebSocket spec.
79       */
80      @Override
81      public boolean isTextDataDecoder()
82      {
83          return true;
84      }
85  
86      @Override
87      public synchronized void outgoingFrame(Frame frame, WriteCallback callback)
88      {
89          if (frame.getType().isControl())
90          {
91              // skip, cannot compress control frames.
92              nextOutgoingFrame(frame,callback);
93              return;
94          }
95  
96          ByteBuffer data = frame.getPayload();
97  
98          // deflate data
99          method.compress().input(data);
100         while (!method.compress().isDone())
101         {
102             ByteBuffer buf = method.compress().process();
103             WebSocketFrame out = new WebSocketFrame(frame).setPayload(buf);
104             out.setRsv1(true);
105             if (!method.compress().isDone())
106             {
107                 out.setFin(false);
108                 nextOutgoingFrame(frame,null); // no callback for start/end frames
109             }
110             else
111             {
112                 nextOutgoingFrame(out,callback); // pass thru callback
113             }
114         }
115 
116         // reset on every frame.
117         method.compress().end();
118     }
119 
120     @Override
121     public void setConfig(ExtensionConfig config)
122     {
123         super.setConfig(config);
124     }
125 
126     @Override
127     public String toString()
128     {
129         return this.getClass().getSimpleName() + "[]";
130     }
131 }