/******************************************************************************* * Copyright (c) 2011 Intalio, Inc. * ====================================================================== * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. *******************************************************************************/ package org.eclipse.jetty.websocket; import java.io.IOException; import java.util.Map; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * TODO Implement proposed deflate frame draft */ public class DeflateFrameExtension extends AbstractExtension { private static final Logger LOG = Log.getLogger(DeflateFrameExtension.class); private int _minLength=8; private Deflater _deflater; private Inflater _inflater; public DeflateFrameExtension() { super("x-deflate-frame"); } @Override public boolean init(Map<String, String> parameters) { if (!parameters.containsKey("minLength")) parameters.put("minLength",Integer.toString(_minLength)); if(super.init(parameters)) { _minLength=getInitParameter("minLength",_minLength); _deflater=new Deflater(); _inflater=new Inflater(); return true; } return false; } /* (non-Javadoc) * @see org.eclipse.jetty.websocket.AbstractExtension#onFrame(byte, byte, org.eclipse.jetty.io.Buffer) */ @Override public void onFrame(byte flags, byte opcode, Buffer buffer) { if (getConnection().isControl(opcode) || !isFlag(flags,1)) { super.onFrame(flags,opcode,buffer); return; } if (buffer.array()==null) buffer=buffer.asMutableBuffer(); int length=0xff&buffer.get(); if (length>=0x7e) { int b=(length==0x7f)?8:2; length=0; while(b-->0) length=0x100*length+(0xff&buffer.get()); } // TODO check a max framesize _inflater.setInput(buffer.array(),buffer.getIndex(),buffer.length()); ByteArrayBuffer buf = new ByteArrayBuffer(length); try { while(_inflater.getRemaining()>0) { int inflated=_inflater.inflate(buf.array(),buf.putIndex(),buf.space()); if (inflated==0) throw new DataFormatException("insufficient data"); buf.setPutIndex(buf.putIndex()+inflated); } super.onFrame(clearFlag(flags,1),opcode,buf); } catch(DataFormatException e) { LOG.warn(e); getConnection().close(WebSocketConnectionRFC6455.CLOSE_BAD_PAYLOAD,e.toString()); } } /* (non-Javadoc) * @see org.eclipse.jetty.websocket.AbstractExtension#addFrame(byte, byte, byte[], int, int) */ @Override public void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException { if (getConnection().isControl(opcode) || length<_minLength) { super.addFrame(clearFlag(flags,1),opcode,content,offset,length); return; } // prepare the uncompressed input _deflater.reset(); _deflater.setInput(content,offset,length); _deflater.finish(); // prepare the output buffer byte[] out= new byte[length]; int out_offset=0; // write the uncompressed length if (length>0xffff) { out[out_offset++]=0x7f; out[out_offset++]=(byte)0; out[out_offset++]=(byte)0; out[out_offset++]=(byte)0; out[out_offset++]=(byte)0; out[out_offset++]=(byte)((length>>24)&0xff); out[out_offset++]=(byte)((length>>16)&0xff); out[out_offset++]=(byte)((length>>8)&0xff); out[out_offset++]=(byte)(length&0xff); } else if (length >=0x7e) { out[out_offset++]=0x7e; out[out_offset++]=(byte)(length>>8); out[out_offset++]=(byte)(length&0xff); } else { out[out_offset++]=(byte)(length&0x7f); } int l = _deflater.deflate(out,out_offset,length-out_offset); if (_deflater.finished()) super.addFrame(setFlag(flags,1),opcode,out,0,l+out_offset); else super.addFrame(clearFlag(flags,1),opcode,content,offset,length); } }