103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.websocket;
2003928aee4356845252ac6b662d5c72c29903813eJake Slack
2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2203928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.Map;
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.zip.DataFormatException;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.zip.Deflater;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.util.zip.Inflater;
2603928aee4356845252ac6b662d5c72c29903813eJake Slack
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.Buffer;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.io.ByteArrayBuffer;
2903928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Log;
3003928aee4356845252ac6b662d5c72c29903813eJake Slackimport org.eclipse.jetty.util.log.Logger;
3103928aee4356845252ac6b662d5c72c29903813eJake Slack
3203928aee4356845252ac6b662d5c72c29903813eJake Slack/**
3303928aee4356845252ac6b662d5c72c29903813eJake Slack * TODO Implement proposed deflate frame draft
3403928aee4356845252ac6b662d5c72c29903813eJake Slack */
3503928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class DeflateFrameExtension extends AbstractExtension
3603928aee4356845252ac6b662d5c72c29903813eJake Slack{
3703928aee4356845252ac6b662d5c72c29903813eJake Slack    private static final Logger LOG = Log.getLogger(DeflateFrameExtension.class);
3803928aee4356845252ac6b662d5c72c29903813eJake Slack
3903928aee4356845252ac6b662d5c72c29903813eJake Slack    private int _minLength=8;
4003928aee4356845252ac6b662d5c72c29903813eJake Slack    private Deflater _deflater;
4103928aee4356845252ac6b662d5c72c29903813eJake Slack    private Inflater _inflater;
4203928aee4356845252ac6b662d5c72c29903813eJake Slack
4303928aee4356845252ac6b662d5c72c29903813eJake Slack    public DeflateFrameExtension()
4403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
4503928aee4356845252ac6b662d5c72c29903813eJake Slack        super("x-deflate-frame");
4603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
4703928aee4356845252ac6b662d5c72c29903813eJake Slack
4803928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
4903928aee4356845252ac6b662d5c72c29903813eJake Slack    public boolean init(Map<String, String> parameters)
5003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
5103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (!parameters.containsKey("minLength"))
5203928aee4356845252ac6b662d5c72c29903813eJake Slack            parameters.put("minLength",Integer.toString(_minLength));
5303928aee4356845252ac6b662d5c72c29903813eJake Slack        if(super.init(parameters))
5403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
5503928aee4356845252ac6b662d5c72c29903813eJake Slack            _minLength=getInitParameter("minLength",_minLength);
5603928aee4356845252ac6b662d5c72c29903813eJake Slack
5703928aee4356845252ac6b662d5c72c29903813eJake Slack            _deflater=new Deflater();
5803928aee4356845252ac6b662d5c72c29903813eJake Slack            _inflater=new Inflater();
5903928aee4356845252ac6b662d5c72c29903813eJake Slack
6003928aee4356845252ac6b662d5c72c29903813eJake Slack            return true;
6103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
6203928aee4356845252ac6b662d5c72c29903813eJake Slack        return false;
6303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
6403928aee4356845252ac6b662d5c72c29903813eJake Slack
6503928aee4356845252ac6b662d5c72c29903813eJake Slack    /* (non-Javadoc)
6603928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.websocket.AbstractExtension#onFrame(byte, byte, org.eclipse.jetty.io.Buffer)
6703928aee4356845252ac6b662d5c72c29903813eJake Slack     */
6803928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
6903928aee4356845252ac6b662d5c72c29903813eJake Slack    public void onFrame(byte flags, byte opcode, Buffer buffer)
7003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
7103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (getConnection().isControl(opcode) || !isFlag(flags,1))
7203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
7303928aee4356845252ac6b662d5c72c29903813eJake Slack            super.onFrame(flags,opcode,buffer);
7403928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
7503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
7603928aee4356845252ac6b662d5c72c29903813eJake Slack
7703928aee4356845252ac6b662d5c72c29903813eJake Slack        if (buffer.array()==null)
7803928aee4356845252ac6b662d5c72c29903813eJake Slack            buffer=buffer.asMutableBuffer();
7903928aee4356845252ac6b662d5c72c29903813eJake Slack
8003928aee4356845252ac6b662d5c72c29903813eJake Slack        int length=0xff&buffer.get();
8103928aee4356845252ac6b662d5c72c29903813eJake Slack        if (length>=0x7e)
8203928aee4356845252ac6b662d5c72c29903813eJake Slack        {
8303928aee4356845252ac6b662d5c72c29903813eJake Slack            int b=(length==0x7f)?8:2;
8403928aee4356845252ac6b662d5c72c29903813eJake Slack            length=0;
8503928aee4356845252ac6b662d5c72c29903813eJake Slack            while(b-->0)
8603928aee4356845252ac6b662d5c72c29903813eJake Slack                length=0x100*length+(0xff&buffer.get());
8703928aee4356845252ac6b662d5c72c29903813eJake Slack        }
8803928aee4356845252ac6b662d5c72c29903813eJake Slack
8903928aee4356845252ac6b662d5c72c29903813eJake Slack        // TODO check a max framesize
9003928aee4356845252ac6b662d5c72c29903813eJake Slack
9103928aee4356845252ac6b662d5c72c29903813eJake Slack        _inflater.setInput(buffer.array(),buffer.getIndex(),buffer.length());
9203928aee4356845252ac6b662d5c72c29903813eJake Slack        ByteArrayBuffer buf = new ByteArrayBuffer(length);
9303928aee4356845252ac6b662d5c72c29903813eJake Slack        try
9403928aee4356845252ac6b662d5c72c29903813eJake Slack        {
9503928aee4356845252ac6b662d5c72c29903813eJake Slack            while(_inflater.getRemaining()>0)
9603928aee4356845252ac6b662d5c72c29903813eJake Slack            {
9703928aee4356845252ac6b662d5c72c29903813eJake Slack                int inflated=_inflater.inflate(buf.array(),buf.putIndex(),buf.space());
9803928aee4356845252ac6b662d5c72c29903813eJake Slack                if (inflated==0)
9903928aee4356845252ac6b662d5c72c29903813eJake Slack                    throw new DataFormatException("insufficient data");
10003928aee4356845252ac6b662d5c72c29903813eJake Slack                buf.setPutIndex(buf.putIndex()+inflated);
10103928aee4356845252ac6b662d5c72c29903813eJake Slack            }
10203928aee4356845252ac6b662d5c72c29903813eJake Slack
10303928aee4356845252ac6b662d5c72c29903813eJake Slack            super.onFrame(clearFlag(flags,1),opcode,buf);
10403928aee4356845252ac6b662d5c72c29903813eJake Slack        }
10503928aee4356845252ac6b662d5c72c29903813eJake Slack        catch(DataFormatException e)
10603928aee4356845252ac6b662d5c72c29903813eJake Slack        {
10703928aee4356845252ac6b662d5c72c29903813eJake Slack            LOG.warn(e);
10803928aee4356845252ac6b662d5c72c29903813eJake Slack            getConnection().close(WebSocketConnectionRFC6455.CLOSE_BAD_PAYLOAD,e.toString());
10903928aee4356845252ac6b662d5c72c29903813eJake Slack        }
11003928aee4356845252ac6b662d5c72c29903813eJake Slack    }
11103928aee4356845252ac6b662d5c72c29903813eJake Slack
11203928aee4356845252ac6b662d5c72c29903813eJake Slack    /* (non-Javadoc)
11303928aee4356845252ac6b662d5c72c29903813eJake Slack     * @see org.eclipse.jetty.websocket.AbstractExtension#addFrame(byte, byte, byte[], int, int)
11403928aee4356845252ac6b662d5c72c29903813eJake Slack     */
11503928aee4356845252ac6b662d5c72c29903813eJake Slack    @Override
11603928aee4356845252ac6b662d5c72c29903813eJake Slack    public void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException
11703928aee4356845252ac6b662d5c72c29903813eJake Slack    {
11803928aee4356845252ac6b662d5c72c29903813eJake Slack        if (getConnection().isControl(opcode) || length<_minLength)
11903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
12003928aee4356845252ac6b662d5c72c29903813eJake Slack            super.addFrame(clearFlag(flags,1),opcode,content,offset,length);
12103928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
12203928aee4356845252ac6b662d5c72c29903813eJake Slack        }
12303928aee4356845252ac6b662d5c72c29903813eJake Slack
12403928aee4356845252ac6b662d5c72c29903813eJake Slack        // prepare the uncompressed input
12503928aee4356845252ac6b662d5c72c29903813eJake Slack        _deflater.reset();
12603928aee4356845252ac6b662d5c72c29903813eJake Slack        _deflater.setInput(content,offset,length);
12703928aee4356845252ac6b662d5c72c29903813eJake Slack        _deflater.finish();
12803928aee4356845252ac6b662d5c72c29903813eJake Slack
12903928aee4356845252ac6b662d5c72c29903813eJake Slack        // prepare the output buffer
13003928aee4356845252ac6b662d5c72c29903813eJake Slack        byte[] out= new byte[length];
13103928aee4356845252ac6b662d5c72c29903813eJake Slack        int out_offset=0;
13203928aee4356845252ac6b662d5c72c29903813eJake Slack
13303928aee4356845252ac6b662d5c72c29903813eJake Slack        // write the uncompressed length
13403928aee4356845252ac6b662d5c72c29903813eJake Slack        if (length>0xffff)
13503928aee4356845252ac6b662d5c72c29903813eJake Slack        {
13603928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=0x7f;
13703928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)0;
13803928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)0;
13903928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)0;
14003928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)0;
14103928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)((length>>24)&0xff);
14203928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)((length>>16)&0xff);
14303928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)((length>>8)&0xff);
14403928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)(length&0xff);
14503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
14603928aee4356845252ac6b662d5c72c29903813eJake Slack        else if (length >=0x7e)
14703928aee4356845252ac6b662d5c72c29903813eJake Slack        {
14803928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=0x7e;
14903928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)(length>>8);
15003928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)(length&0xff);
15103928aee4356845252ac6b662d5c72c29903813eJake Slack        }
15203928aee4356845252ac6b662d5c72c29903813eJake Slack        else
15303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
15403928aee4356845252ac6b662d5c72c29903813eJake Slack            out[out_offset++]=(byte)(length&0x7f);
15503928aee4356845252ac6b662d5c72c29903813eJake Slack        }
15603928aee4356845252ac6b662d5c72c29903813eJake Slack
15703928aee4356845252ac6b662d5c72c29903813eJake Slack        int l = _deflater.deflate(out,out_offset,length-out_offset);
15803928aee4356845252ac6b662d5c72c29903813eJake Slack
15903928aee4356845252ac6b662d5c72c29903813eJake Slack        if (_deflater.finished())
16003928aee4356845252ac6b662d5c72c29903813eJake Slack            super.addFrame(setFlag(flags,1),opcode,out,0,l+out_offset);
16103928aee4356845252ac6b662d5c72c29903813eJake Slack        else
16203928aee4356845252ac6b662d5c72c29903813eJake Slack            super.addFrame(clearFlag(flags,1),opcode,content,offset,length);
16303928aee4356845252ac6b662d5c72c29903813eJake Slack    }
16403928aee4356845252ac6b662d5c72c29903813eJake Slack}
165