/** * Copyright (C) 2010-2012, FuseSource Corp. All rights reserved. * * http://fusesource.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fusesource.mqtt.codec; import org.fusesource.hawtbuf.Buffer; import org.fusesource.hawtdispatch.transport.AbstractProtocolCodec; import org.fusesource.hawtdispatch.util.BufferPools; import java.io.IOException; import java.nio.ByteBuffer; /** * <p> * </p> * * @author <a href="http://hiramchirino.com">Hiram Chirino</a> */ public class MQTTProtocolCodec extends AbstractProtocolCodec { private static final BufferPools BUFFER_POOLS = new BufferPools(); private int maxMessageLength = 1024*1024*100; public MQTTProtocolCodec() { this.bufferPools = BUFFER_POOLS; } public int getMaxMessageLength() { return maxMessageLength; } public void setMaxMessageLength(int maxMessageLength) { this.maxMessageLength = maxMessageLength; } @Override protected void encode(Object value) throws IOException { MQTTFrame frame = (MQTTFrame) value; nextWriteBuffer.write(frame.header()); int remaining = 0; for(Buffer buffer : frame.buffers) { remaining += buffer.length; } do { byte digit = (byte) (remaining & 0x7F); remaining >>>= 7; if (remaining > 0) { digit |= 0x80; } nextWriteBuffer.write(digit); } while (remaining > 0); for(Buffer buffer : frame.buffers) { nextWriteBuffer.write(buffer.data, buffer.offset, buffer.length); } } @Override protected Action initialDecodeAction() { return readHeader; } private final Action readHeader = new Action() { public MQTTFrame apply() throws IOException { int length = readLength(); if( length >= 0 ) { if( length > maxMessageLength) { throw new IOException("The maximum message length was exceeded"); } byte header = readBuffer.get(readStart); readStart = readEnd; if( length > 0 ) { nextDecodeAction = readBody(header, length); } else { return new MQTTFrame().header(header); } } return null; } }; private int readLength() throws IOException { readEnd = readStart+2; // Header is at least 2 bytes.. int limit = readBuffer.position(); int length = 0; int multiplier = 1; byte digit; while (readEnd-1 < limit) { // last byte is part of the encoded length.. digit = readBuffer.get(readEnd-1); length += (digit & 0x7F) * multiplier; if( (digit & 0x80) == 0 ) { return length; } // length extends out one more byte.. multiplier <<= 7; readEnd++; } return -1; } Action readBody(final byte header, final int length) { return new Action() { public MQTTFrame apply() throws IOException { int limit = readBuffer.position(); if ((limit - readStart) < length) { readEnd = limit; return null; } else { Buffer body = new Buffer(readBuffer.array(), readStart, length); readEnd = readStart = readStart + length; nextDecodeAction = readHeader; return new MQTTFrame(body).header(header); } } }; } }