View Javadoc

1   /*******************************************************************************
2    * Copyright (c) 2011 Intalio, Inc.
3    * ======================================================================
4    * All rights reserved. This program and the accompanying materials
5    * are made available under the terms of the Eclipse Public License v1.0
6    * and Apache License v2.0 which accompanies this distribution.
7    *
8    *   The Eclipse Public License is available at
9    *   http://www.eclipse.org/legal/epl-v10.html
10   *
11   *   The Apache License v2.0 is available at
12   *   http://www.opensource.org/licenses/apache2.0.php
13   *
14   * You may elect to redistribute this code under either of these licenses.
15   *******************************************************************************/
16  package org.eclipse.jetty.websocket;
17  
18  import java.io.IOException;
19  import java.util.Map;
20  import java.util.zip.DataFormatException;
21  import java.util.zip.Deflater;
22  import java.util.zip.Inflater;
23  
24  import org.eclipse.jetty.io.Buffer;
25  import org.eclipse.jetty.io.ByteArrayBuffer;
26  import org.eclipse.jetty.util.log.Log;
27  import org.eclipse.jetty.util.log.Logger;
28  
29  /**
30   * @TODO Implement proposed deflate frame draft
31   */
32  public class DeflateFrameExtension extends AbstractExtension
33  {
34      private static final Logger LOG = Log.getLogger(DeflateFrameExtension.class);
35  
36      private int _minLength=8;
37      private Deflater _deflater;
38      private Inflater _inflater;
39  
40      public DeflateFrameExtension()
41      {
42          super("x-deflate-frame");
43      }
44  
45      @Override
46      public boolean init(Map<String, String> parameters)
47      {
48          if (!parameters.containsKey("minLength"))
49              parameters.put("minLength",Integer.toString(_minLength));
50          if(super.init(parameters))
51          {
52              _minLength=getInitParameter("minLength",_minLength);
53  
54              _deflater=new Deflater();
55              _inflater=new Inflater();
56  
57              return true;
58          }
59          return false;
60      }
61  
62      /* (non-Javadoc)
63       * @see org.eclipse.jetty.websocket.AbstractExtension#onFrame(byte, byte, org.eclipse.jetty.io.Buffer)
64       */
65      @Override
66      public void onFrame(byte flags, byte opcode, Buffer buffer)
67      {
68          if (getConnection().isControl(opcode) || !isFlag(flags,1))
69          {
70              super.onFrame(flags,opcode,buffer);
71              return;
72          }
73  
74          if (buffer.array()==null)
75              buffer=buffer.asMutableBuffer();
76  
77          int length=0xff&buffer.get();
78          if (length>=0x7e)
79          {
80              int b=(length==0x7f)?8:2;
81              length=0;
82              while(b-->0)
83                  length=0x100*length+(0xff&buffer.get());
84          }
85  
86          // TODO check a max framesize
87  
88          _inflater.setInput(buffer.array(),buffer.getIndex(),buffer.length());
89          ByteArrayBuffer buf = new ByteArrayBuffer(length);
90          try
91          {
92              while(_inflater.getRemaining()>0)
93              {
94                  int inflated=_inflater.inflate(buf.array(),buf.putIndex(),buf.space());
95                  if (inflated==0)
96                      throw new DataFormatException("insufficient data");
97                  buf.setPutIndex(buf.putIndex()+inflated);
98              }
99  
100             super.onFrame(clearFlag(flags,1),opcode,buf);
101         }
102         catch(DataFormatException e)
103         {
104             LOG.warn(e);
105             getConnection().close(WebSocketConnectionRFC6455.CLOSE_BAD_PAYLOAD,e.toString());
106         }
107     }
108 
109     /* (non-Javadoc)
110      * @see org.eclipse.jetty.websocket.AbstractExtension#addFrame(byte, byte, byte[], int, int)
111      */
112     @Override
113     public void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException
114     {
115         if (getConnection().isControl(opcode) || length<_minLength)
116         {
117             super.addFrame(clearFlag(flags,1),opcode,content,offset,length);
118             return;
119         }
120 
121         // prepare the uncompressed input
122         _deflater.reset();
123         _deflater.setInput(content,offset,length);
124         _deflater.finish();
125 
126         // prepare the output buffer
127         byte[] out= new byte[length];
128         int out_offset=0;
129 
130         // write the uncompressed length
131         if (length>0xffff)
132         {
133             out[out_offset++]=0x7f;
134             out[out_offset++]=(byte)0;
135             out[out_offset++]=(byte)0;
136             out[out_offset++]=(byte)0;
137             out[out_offset++]=(byte)0;
138             out[out_offset++]=(byte)((length>>24)&0xff);
139             out[out_offset++]=(byte)((length>>16)&0xff);
140             out[out_offset++]=(byte)((length>>8)&0xff);
141             out[out_offset++]=(byte)(length&0xff);
142         }
143         else if (length >=0x7e)
144         {
145             out[out_offset++]=0x7e;
146             out[out_offset++]=(byte)(length>>8);
147             out[out_offset++]=(byte)(length&0xff);
148         }
149         else
150         {
151             out[out_offset++]=(byte)(length&0x7f);
152         }
153 
154         int l = _deflater.deflate(out,out_offset,length-out_offset);
155 
156         if (_deflater.finished())
157             super.addFrame(setFlag(flags,1),opcode,out,0,l+out_offset);
158         else
159             super.addFrame(clearFlag(flags,1),opcode,content,offset,length);
160     }
161 }