Package org.freeswitch.esl.client.transport.message

Source Code of org.freeswitch.esl.client.transport.message.EslFrameDecoder

/*
* Copyright 2010 david varnes.
*
* 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.freeswitch.esl.client.transport.message;

import org.freeswitch.esl.client.internal.HeaderParser;
import org.freeswitch.esl.client.transport.message.EslHeaders.Name;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.TooLongFrameException;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Decoder used by the IO processing pipeline. Client consumers should never need to use
* this class.
* <p>
* Follows the following decode algorithm (from FreeSWITCH wiki)
* <pre>
*    Look for \n\n in your receive buffer
*
*    Examine data for existence of Content-Length
*
*    If NOT present, process event and remove from receive buffer
*    IF present, Shift buffer to remove 'header'
*    Evaluate content-length value
*   
*    Loop until receive buffer size is >= Content-length
*    Extract content-length bytes from buffer and process
* </pre>
*
* @author  david varnes
*/
public class EslFrameDecoder extends ReplayingDecoder<EslFrameDecoder.State>
{
    /**
     * Line feed character
     */
    static final byte LF = 10;

    protected static enum State
    {
        READ_HEADER,
        READ_BODY,
    }
   
    private final Logger log = LoggerFactory.getLogger( this.getClass() );
    private final int maxHeaderSize;
    private EslMessage currentMessage;
    private boolean treatUnknownHeadersAsBody = false;
   
    public EslFrameDecoder( int maxHeaderSize )
    {
        super( State.READ_HEADER );
        if (maxHeaderSize <= 0)
        {
            throw new IllegalArgumentException(
                    "maxHeaderSize must be a positive integer: " +
                    maxHeaderSize);
        }
        this.maxHeaderSize = maxHeaderSize;
    }
   
    public EslFrameDecoder( int maxHeaderSize, boolean treatUnknownHeadersAsBody )
    {
        this( maxHeaderSize );
        this.treatUnknownHeadersAsBody = treatUnknownHeadersAsBody;
    }
   
    @Override
    protected Object decode( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer,
            State state ) throws Exception
    {
        log.trace( "decode() : state [{}]", state );
        switch ( state )
        {
        case READ_HEADER:
            if ( currentMessage == null )
            {
                currentMessage = new EslMessage();
            }
            /*
             *  read '\n' terminated lines until reach a single '\n'
             */
            boolean reachedDoubleLF = false;
            while ( ! reachedDoubleLF )
            {
                // this will read or fail
                String headerLine = readToLineFeedOrFail( buffer, maxHeaderSize );
                log.debug( "read header line [{}]", headerLine );
                if ( ! headerLine.isEmpty() )
                {
                    // split the header line
                    String[] headerParts = HeaderParser.splitHeader( headerLine );
                    Name headerName = Name.fromLiteral( headerParts[0] );
                    if ( headerName == null )
                    {
                        if ( treatUnknownHeadersAsBody )
                        {
                            // cache this 'header' as a body line <-- useful for Outbound client mode
                            currentMessage.addBodyLine( headerLine );
                        }
                        else
                        {
                            throw new IllegalStateException( "Unhandled ESL header [" + headerParts[0] + ']' );
                        }
                    }
                    currentMessage.addHeader( headerName, headerParts[1] );
                }
                else
                {
                    reachedDoubleLF = true;
                }
                // do not read in this line again
                checkpoint();
            }
            // have read all headers - check for content-length
            if ( currentMessage.hasContentLength() )
            {
                checkpoint( State.READ_BODY );
                log.debug( "have content-length, decoding body .." );
                //  force the next section

                return null;
            }
            else
            {
                // end of message
                checkpoint( State.READ_HEADER );
                // send message upstream
                EslMessage decodedMessage = currentMessage;
                currentMessage = null;
               
                return decodedMessage;
            }

        case READ_BODY:
            /*
             *   read the content-length specified
             */
            int contentLength = currentMessage.getContentLength();
            ChannelBuffer bodyBytes = buffer.readBytes( contentLength );
            log.debug( "read [{}] body bytes", bodyBytes.writerIndex() );
            // most bodies are line based, so split on LF
            while( bodyBytes.readable() )
            {
                String bodyLine = readLine( bodyBytes, contentLength );
                log.debug( "read body line [{}]", bodyLine );
                currentMessage.addBodyLine( bodyLine );
            }
           
            // end of message
            checkpoint( State.READ_HEADER );
            // send message upstream
            EslMessage decodedMessage = currentMessage;
            currentMessage = null;
           
            return decodedMessage;
           
        default:
            throw new Error( "Illegal state: [" + state  + ']' );
        }
    }

    private String readToLineFeedOrFail( ChannelBuffer buffer, int maxLineLegth ) throws TooLongFrameException
    {
        StringBuilder sb = new StringBuilder(64);
        while ( true )
        {
            // this read might fail
            byte nextByte = buffer.readByte();
            if ( nextByte == LF )
            {
                return sb.toString();
            }
            else
            {
                // Abort decoding if the decoded line is too large.
                if ( sb.length() >=  maxLineLegth )
                {
                    throw new TooLongFrameException(
                            "ESL header line is longer than " + maxLineLegth + " bytes.");
                }
                sb.append( (char) nextByte );
            }
        }
    }
  
    private String readLine( ChannelBuffer buffer, int maxLineLength ) throws TooLongFrameException
    {
        StringBuilder sb = new StringBuilder(64);
        while ( buffer.readable() )
        {
            // this read should always succeed
            byte nextByte = buffer.readByte();
            if (nextByte == LF)
            {
                return sb.toString();
            }
            else
            {
                // Abort decoding if the decoded line is too large.
                if ( sb.length() >= maxLineLength )
                {
                    throw new TooLongFrameException(
                            "ESL message line is longer than " + maxLineLength + " bytes.");
                }
                sb.append( (char) nextByte );
            }
        }
       
        return sb.toString();
    }
}
TOP

Related Classes of org.freeswitch.esl.client.transport.message.EslFrameDecoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.