All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nist.javax.sip.parser.PipelinedMsgParser Maven / Gradle / Ivy

There is a newer version: 1.3.0-91
Show newest version
/*
 * Conditions Of Use
 *
 * This software was developed by employees of the National Institute of
 * Standards and Technology (NIST), an agency of the Federal Government.
 * Pursuant to title 15 Untied States Code Section 105, works of NIST
 * employees are not subject to copyright protection in the United States
 * and are considered to be in the public domain.  As a result, a formal
 * license is not needed to use the software.
 *
 * This software is provided by NIST as a service and is expressly
 * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
 * AND DATA ACCURACY.  NIST does not warrant or make any representations
 * regarding the use of the software or the results thereof, including but
 * not limited to the correctness, accuracy, reliability or usefulness of
 * the software.
 *
 * Permission to use this software is contingent upon your acceptance
 * of the terms of this agreement
 *
 * .
 *
 */
/******************************************************************************
 * Product of NIST/ITL Advanced Networking Technologies Division (ANTD)       *
 ******************************************************************************/
package gov.nist.javax.sip.parser;

/*
 *
 * Lamine Brahimi and Yann Duponchel (IBM Zurich) noticed that the parser was
 * blocking so I threw out some cool pipelining which ran fast but only worked
 * when the phase of the moon matched its mood. Now things are serialized and
 * life goes slower but more reliably.
 *
 */
import gov.nist.core.CommonLogger;
import gov.nist.core.InternalErrorHandler;
import gov.nist.core.LogLevels;
import gov.nist.core.LogWriter;
import gov.nist.core.StackLogger;
import gov.nist.javax.sip.header.ContentLength;
import gov.nist.javax.sip.message.SIPMessage;
import gov.nist.javax.sip.stack.BlockingQueueDispatchAuditor;
import gov.nist.javax.sip.stack.ConnectionOrientedMessageChannel;
import gov.nist.javax.sip.stack.QueuedMessageDispatchBase;
import gov.nist.javax.sip.stack.SIPTransactionStack;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * This implements a pipelined message parser suitable for use with a stream -
 * oriented input such as TCP. The client uses this class by instatiating with
 * an input stream from which input is read and fed to a message parser. It
 * keeps reading from the input stream and process messages in a never ending
 * interpreter loop. The message listener interface gets called for processing
 * messages or for processing errors. The payload specified by the
 * content-length header is read directly from the input stream. This can be
 * accessed from the SIPMessage using the getContent and getContentBytes methods
 * provided by the SIPMessage class.
 *
 * @version 1.2 $Revision: 1.39 $ $Date: 2010-12-02 22:04:22 $
 *
 * @author M. Ranganathan
 *
 * @see SIPMessageListener
 */
public final class PipelinedMsgParser implements Runnable {
	private static StackLogger logger = CommonLogger.getLogger(PipelinedMsgParser.class);

	private static final String CRLF = "\r\n";

    /**
     * The message listener that is registered with this parser. (The message
     * listener has methods that can process correct and erroneous messages.)
     */
    protected SIPMessageListener sipMessageListener;
    private Thread mythread; // Preprocessor thread
    //private byte[] messageBody;
    //private boolean errorFlag;
    private Pipeline rawInputStream;
    private int maxMessageSize;
    private int sizeCounter;
    private SIPTransactionStack sipStack;
    private MessageParser smp = null;
    private ConcurrentHashMap messagesOrderingMap = new ConcurrentHashMap();
    boolean isRunning = false;
    
    /**
     * default constructor.
     */
    protected PipelinedMsgParser() {
        super();        
    }

    private static int uid = 0;

    private static synchronized int getNewUid() {
        return uid++;
    }

    /**
     * Constructor when we are given a message listener and an input stream
     * (could be a TCP connection or a file)
     *
     * @param sipMessageListener
     *            Message listener which has methods that get called back from
     *            the parser when a parse is complete
     * @param in
     *            Input stream from which to read the input.
     * @param debug
     *            Enable/disable tracing or lexical analyser switch.
     */
    public PipelinedMsgParser(SIPTransactionStack sipStack, SIPMessageListener sipMessageListener,
            Pipeline in, boolean debug, int maxMessageSize) {
        this();
        this.sipStack = sipStack;
        smp = sipStack.getMessageParserFactory().createMessageParser(sipStack);
        this.sipMessageListener = sipMessageListener;
        rawInputStream = in;
        this.maxMessageSize = maxMessageSize;
        mythread = new Thread(this);
        mythread.setName("PipelineThread-" + getNewUid());

    }

    /**
     * This is the constructor for the pipelined parser.
     *
     * @param mhandler
     *            a SIPMessageListener implementation that provides the message
     *            handlers to handle correctly and incorrectly parsed messages.
     * @param in
     *            An input stream to read messages from.
     */

    public PipelinedMsgParser(SIPTransactionStack sipStack, SIPMessageListener mhandler, Pipeline in,
            int maxMsgSize) {
        this(sipStack, mhandler, in, false, maxMsgSize);
    }

    /**
     * This is the constructor for the pipelined parser.
     *
     * @param in -
     *            An input stream to read messages from.
     */

    public PipelinedMsgParser(SIPTransactionStack sipStack, Pipeline in) {
        this(sipStack, null, in, false, 0);
    }

    /**
     * Start reading and processing input.
     */
    public void processInput() {
        mythread.start();
    }

    /**
     * Create a new pipelined parser from an existing one.
     *
     * @return A new pipelined parser that reads from the same input stream.
     */
    protected Object clone() {
        PipelinedMsgParser p = new PipelinedMsgParser();

        p.rawInputStream = this.rawInputStream;
        p.sipMessageListener = this.sipMessageListener;
        Thread mythread = new Thread(p);
        mythread.setName("PipelineThread");
        return p;
    }

    /**
     * Add a class that implements a SIPMessageListener interface whose methods
     * get called * on successful parse and error conditons.
     *
     * @param mlistener
     *            a SIPMessageListener implementation that can react to correct
     *            and incorrect pars.
     */

    public void setMessageListener(SIPMessageListener mlistener) {
        sipMessageListener = mlistener;
    }

    /**
     * read a line of input. Note that we encode the result in UTF-8
     */
  
    
    private String readLine(InputStream inputStream) throws IOException {
        int counter = 0;
        int increment = 1024;
        int bufferSize = increment;
        byte[] lineBuffer = new byte[bufferSize];
        // handles RFC 5626 CRLF keepalive mechanism
        byte[] crlfBuffer = new byte[2];
        int crlfCounter = 0;
        while (true) {
            char ch;
            int i = inputStream.read();
            if (i == -1) {
                throw new IOException("End of stream");
            } else
                ch = (char) ( i & 0xFF);
            // reduce the available read size by 1 ("size" of a char).
            if (this.maxMessageSize > 0) {
                this.sizeCounter--;
                if (this.sizeCounter <= 0)
                    throw new IOException("Max size exceeded!");
            }
            if (ch != '\r')
                lineBuffer[counter++] = (byte) (i&0xFF);
            else if (counter == 0)            	
            	crlfBuffer[crlfCounter++] = (byte) '\r';
                       
            if (ch == '\n') {
            	if(counter == 1 && crlfCounter > 0) {
            		crlfBuffer[crlfCounter++] = (byte) '\n';            		
            	} 
            	break;            	
            }
            
            if( counter == bufferSize ) {
                byte[] tempBuffer = new byte[bufferSize + increment];
                System.arraycopy((Object)lineBuffer,0, (Object)tempBuffer, 0, bufferSize);
                bufferSize = bufferSize + increment;
                lineBuffer = tempBuffer;
                
            }
        }
        if(counter == 1 && crlfCounter > 0) {
        	return new String(crlfBuffer,0,crlfCounter,"UTF-8");
        } else {
        	return new String(lineBuffer,0,counter,"UTF-8");
        }
        
    }
    
    public class Dispatch implements Runnable, QueuedMessageDispatchBase{
    	CallIDOrderingStructure callIDOrderingStructure;
    	String callId;
    	long time;
    	public Dispatch(CallIDOrderingStructure callIDOrderingStructure, String callId) {
    		this.callIDOrderingStructure = callIDOrderingStructure;
    		this.callId = callId;
    		time = System.currentTimeMillis();
    	}
        public void run() {   
        	
            // we acquire it in the thread to avoid blocking other messages with a different call id
            // that could be processed in parallel                                    
            Semaphore semaphore = callIDOrderingStructure.getSemaphore();
            final Queue messagesForCallID = callIDOrderingStructure.getMessagesForCallID();
            if(sipStack.sipEventInterceptor != null) {
            	sipStack.sipEventInterceptor.beforeMessage(messagesForCallID.peek());
            }
            try {                                                                                
                semaphore.acquire();                                        
            } catch (InterruptedException e) {
                logger.logError("Semaphore acquisition for callId " + callId + " interrupted", e);
            }
            // once acquired we get the first message to process
            SIPMessage message = messagesForCallID.poll();
            if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
            	logger.logDebug("semaphore acquired for message " + message);
            }
            
            try {
                sipMessageListener.processMessage(message);
            } catch (Exception e) {
            	logger.logError("Error occured processing message", e);    
                // We do not break the TCP connection because other calls use the same socket here
            } finally {                                        
                if(messagesForCallID.size() <= 0) {
                    messagesOrderingMap.remove(callId);
                    if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
                    	logger.logDebug("CallIDOrderingStructure removed for message " + callId);
                    }
                }
                if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
                	logger.logDebug("releasing semaphore for message " + message);
                }
                //release the semaphore so that another thread can process another message from the call id queue in the correct order
                // or a new message from another call id queue
                semaphore.release(); 
                if(messagesOrderingMap.isEmpty()) {
                    synchronized (messagesOrderingMap) {
                        messagesOrderingMap.notify();
                    }
                }
                if(sipStack.sipEventInterceptor != null) {
                	sipStack.sipEventInterceptor.afterMessage(message);
                }
            }
            if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
            	logger.logDebug("dispatch task done on " + message + " threadname " + mythread.getName());
            }
        }
		public long getReceptionTime() {
			return time;
		}
    };
    /**
     * This is input reading thread for the pipelined parser. You feed it input
     * through the input stream (see the constructor) and it calls back an event
     * listener interface for message processing or error. It cleans up the
     * input - dealing with things like line continuation
     */
    public void run() {

        Pipeline inputStream = this.rawInputStream;
        final StackLogger stackLogger = logger;
        // inputStream = new MyFilterInputStream(this.rawInputStream);
        // I cannot use buffered reader here because we may need to switch
        // encodings to read the message body.
        try {
            isRunning = true;
            while (isRunning) {
                this.sizeCounter = this.maxMessageSize;
                // this.messageSize = 0;
                StringBuilder inputBuffer = new StringBuilder();

                if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
                	logger.logDebug("Starting to parse.");
                }

                String line1;
                String line2 = null;
                boolean isPreviousLineCRLF = false;
                while (true) {
                    try {
                        line1 = readLine(inputStream);
                        // ignore blank lines.
                        if (line1.equals("\n")) {
                        	if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
                            	logger.logDebug("Discarding blank line");
                            }
                            continue;
                        } else if(CRLF.equals(line1)) {
                        	if(isPreviousLineCRLF) {
                        		// Handling keepalive ping (double CRLF) as defined per RFC 5626 Section 4.4.1
                            	// sending pong (single CRLF)
                            	if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) {
                                    logger.logDebug("KeepAlive Double CRLF received, sending single CRLF as defined per RFC 5626 Section 4.4.1");
                                    logger.logDebug("~~~ setting isPreviousLineCRLF=false");
                                }

                                isPreviousLineCRLF = false;

                            	try {
            						sipMessageListener.sendSingleCLRF();
            					} catch (Exception e) {						
            						logger.logError("A problem occured while trying to send a single CLRF in response to a double CLRF", e);
            					}                	
                            	continue;
                        	} else {
	                        	isPreviousLineCRLF = true;
	                        	if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
	                            	logger.logDebug("Received CRLF");
	                            }
	                        	if(sipMessageListener != null && 
	                        			sipMessageListener instanceof ConnectionOrientedMessageChannel) {
	                        		((ConnectionOrientedMessageChannel)sipMessageListener).cancelPingKeepAliveTimeoutTaskIfStarted();
	                        	}
                        	}
                        	continue;
                        } else 
                            break;
                    } catch (IOException ex) {
                        // we only wait if the thread is still in a running state and hasn't been close from somewhere else
                    	// or we are leaking because the thread is waiting forever
                    	if(PostParseExecutorServices.getPostParseExecutor() != null && isRunning){
                    		if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG))
                                logger.logDebug("waiting for messagesOrderingMap " + this + " threadname " + mythread.getName());
                            synchronized (messagesOrderingMap) {
                                try {
                                    messagesOrderingMap.wait(64000);
                                } catch (InterruptedException e) {}                                
                            }  
                            if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG))
                                logger.logDebug("got notified for messagesOrderingMap " + this + " threadname " + mythread.getName());                            
                        }
                        this.rawInputStream.stopTimer();
                        if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
                        	logger.logDebug("thread ending for threadname " + mythread.getName());
                        }
                        if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
                        	logger.logStackTrace(LogLevels.TRACE_DEBUG);
                        }                        
                        return;
                    }
                }

                inputBuffer.append(line1);
                // Guard against bad guys.
                this.rawInputStream.startTimer();
                int bytesRead = 0;
                if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
                	logger.logDebug("Reading Input stream.");
                }
                while (true) {
                    try {
                        line2 = readLine(inputStream);
                        bytesRead += line2.length();
                        if(maxMessageSize>0 && bytesRead> (maxMessageSize/2) ) throw new IOException("Pre-content-length headers size exceeded. The size of the message of the headers prior to Content-Length is too large. This must be an invalid message. Limit is MAX_MESSAGE_SIZE/2=" + maxMessageSize/2);
                        inputBuffer.append(line2);
                        if (line2.trim().equals(""))
                            break;
                    } catch (IOException ex) {
                        // we only wait if the thread is still in a running state and hasn't been close from somewhere else
                    	// or we are leaking because the thread is waiting forever
                    	if(PostParseExecutorServices.getPostParseExecutor() != null && isRunning){
                    		if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG))
                                logger.logDebug("waiting for messagesOrderingMap " + this + " threadname " + mythread.getName());
                            synchronized (messagesOrderingMap) {
                                try {
                                    messagesOrderingMap.wait(64000);
                                } catch (InterruptedException e) {}                                
                            }  
                            if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG))
                                logger.logDebug("got notified for messagesOrderingMap " + this + " threadname " + mythread.getName());                            
                        }
                        this.rawInputStream.stopTimer();
                        if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
                        	logger.logDebug("thread ending for threadname " + mythread.getName());
                        }
                        if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
                        	logger.logStackTrace(LogLevels.TRACE_DEBUG);
                        }                        
                        return;
                    }
                }

                // Stop the timer that will kill the read.
                this.rawInputStream.stopTimer();
                inputBuffer.append(line2);               
//                smp.setParseExceptionListener(sipMessageListener);
//                smp.setReadBody(false);
                SIPMessage sipMessage = null;

                try {
                    if (stackLogger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
                        stackLogger.logDebug("About to parse : " + inputBuffer.toString());
                    }
                    byte[] inputBufferBytes;
                    try {
                        inputBufferBytes = inputBuffer.toString().getBytes("UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        // fall back to default encoding. The lack of UTF8 support has been logged at SIP stack startup
                        inputBufferBytes = inputBuffer.toString().getBytes();
                    }
                    sipMessage = smp.parseSIPMessage(inputBufferBytes, false, false, sipMessageListener);
                    if (sipMessage == null) {
                        this.rawInputStream.stopTimer();
                        continue;
                    }
                } catch (ParseException ex) {
                    // Just ignore the parse exception.
                	if (logger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
	            		logger.logDebug("Detected a parse error", ex);
	            	}
                    continue;
                }

                if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
                	logger.logDebug("Completed parsing message");
                }
                String clString = sipMessage.getHeaderAsFormattedString(ContentLength.NAME);
                if(clString.length()>30) throw new RuntimeException("Bad content lenght header " + clString);
                ContentLength cl = (ContentLength) sipMessage
                        .getContentLength();
                
             
                int contentLength = 0;
                if (cl != null) {
                    contentLength = cl.getContentLength();
                } else {
                    contentLength = 0;
                }

                if (logger.isLoggingEnabled(LogLevels.TRACE_DEBUG)) {
                	logger.logDebug("Content length = " + contentLength);
                }
                
                if(maxMessageSize > 0 && contentLength > maxMessageSize) throw new RuntimeException("Max content size Exceeded! :" + contentLength + " allowed max size is " + maxMessageSize);

                if (contentLength == 0) {
                    sipMessage.removeContent();
                } else if (maxMessageSize == 0
                        || contentLength < this.sizeCounter) {
                    byte[] message_body = new byte[contentLength];
                    int nread = 0;
                    while (nread < contentLength) {
                        // Start my starvation timer.
                        // This ensures that the other end
                        // writes at least some data in
                        // or we will close the pipe from
                        // him. This prevents DOS attack
                        // that takes up all our connections.
                        this.rawInputStream.startTimer();
                        try {

                            int readlength = inputStream.read(message_body,
                                    nread, contentLength - nread);
                            if (readlength > 0) {
                                nread += readlength;
                            } else {
                                break;
                            }
                        } catch (IOException ex) {
                            stackLogger.logError("Exception Reading Content",ex);
                            break;
                        } finally {
                            // Stop my starvation timer.
                            this.rawInputStream.stopTimer();
                        }
                    }
                    sipMessage.setMessageContent(message_body);
                }
                // Content length too large - process the message and
                // return error from there.
                if (sipMessageListener != null) {
                    try {
                        if(PostParseExecutorServices.getPostParseExecutor() == null) {
                        	
                            /**
                             * If gov.nist.javax.sip.TCP_POST_PARSING_THREAD_POOL_SIZE is disabled
                             * we continue with the old logic here.
                             */
                        	if(sipStack.sipEventInterceptor != null) {
                            	sipStack.sipEventInterceptor.beforeMessage(sipMessage);
                            }
                            sipMessageListener.processMessage(sipMessage);
                            if(sipStack.sipEventInterceptor != null) {
                            	sipStack.sipEventInterceptor.afterMessage(sipMessage);
                            }
                        } else {
                            /**
                             * gov.nist.javax.sip.TCP_POST_PARSING_THREAD_POOL_SIZE is enabled so
                             * we use the threadpool to execute the task.
                             */
                            // we need to guarantee message ordering on the same socket on TCP
                            // so we lock and queue of messages per Call Id
                            
                            final String callId = sipMessage.getCallId().getCallId();
                            // http://dmy999.com/article/34/correct-use-of-concurrenthashmap
                            CallIDOrderingStructure orderingStructure = messagesOrderingMap.get(callId);
                            if(orderingStructure == null) {
                                CallIDOrderingStructure newCallIDOrderingStructure = new CallIDOrderingStructure();
                                orderingStructure = messagesOrderingMap.putIfAbsent(callId, newCallIDOrderingStructure);
                                if(orderingStructure == null) {
                                    orderingStructure = newCallIDOrderingStructure;       
                                    if (stackLogger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
                                        stackLogger.logDebug("new CallIDOrderingStructure added for message " + sipMessage);
                                    }
                                }
                            }
                            final CallIDOrderingStructure callIDOrderingStructure = orderingStructure;                                 
                            // we add the message to the pending queue of messages to be processed for that call id here 
                            // to avoid blocking other messages with a different call id
                            // that could be processed in parallel
                            callIDOrderingStructure.getMessagesForCallID().offer(sipMessage);                                                                                   
                            
                            PostParseExecutorServices.getPostParseExecutor().execute(new Dispatch(callIDOrderingStructure, callId)); // run in executor thread
                        }
                    } catch (Exception ex) {
                        // fatal error in processing - close the
                        // connection.
                        break;
                    }
                }
            }
        } finally {
            try {
                cleanMessageOrderingMap();
                if(!inputStream.isClosed()) {
            		inputStream.close();
            	}
            } catch (IOException e) {
                InternalErrorHandler.handleException(e);
            }
        }
    }

    /**
     * Data structure to make sure ordering of Messages is guaranteed under TCP when the post parsing thread pool is used
     * @author [email protected]
     *
     */
    class CallIDOrderingStructure {
        private Semaphore semaphore;
        private Queue messagesForCallID;
        
        public CallIDOrderingStructure() {
            semaphore = new Semaphore(1, true);
            messagesForCallID = new ConcurrentLinkedQueue();
        }        

        /**
         * @return the semaphore
         */
        public Semaphore getSemaphore() {
            return semaphore;
        }
       
        /**
         * @return the messagesForCallID
         */
        public Queue getMessagesForCallID() {
            return messagesForCallID;
        }
    }

    public void close() {
        isRunning = false;
    	if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG))
            logger.logDebug("Closing pipelinedmsgparser " + this + " threadname " + mythread.getName());
        try {
            this.rawInputStream.close();            
        } catch (IOException ex) {
        	if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG))
                logger.logDebug("Couldn't close the rawInputStream " + this + " threadname " + mythread.getName() + " already closed ? " + rawInputStream.isClosed());
            // Ignore.
        }                
        if(PostParseExecutorServices.getPostParseExecutor() != null){
        	cleanMessageOrderingMap();
        	synchronized (mythread) {
            	mythread.notifyAll();
            	//interrupting because there is a race condition on the messagesOrderingMap.wait() that
            	// eventually leads to thread leaking and OutOfMemory
            	mythread.interrupt();
    		} 
        }         
    }
    
    private void cleanMessageOrderingMap() {        
    	// not needed and can cause NPE on close if race condition
//    	for (CallIDOrderingStructure callIDOrderingStructure: messagesOrderingMap.values()) {
//			callIDOrderingStructure.getSemaphore().release();
//			callIDOrderingStructure.getMessagesForCallID().clear();
//		}
        messagesOrderingMap.clear();
        synchronized (messagesOrderingMap) {
            messagesOrderingMap.notifyAll();
        }
    	if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG))
            logger.logDebug("cleaned the messagesOrderingMap " + this + " threadname " + mythread.getName());
    }
}
/*
 * $Log: not supported by cvs2svn $
 * Revision 1.38  2010/12/02 11:44:16  vralev
 * Issue number:  346
 * Obtained from: vralev
 *
 * Congestion control. Patch + Tests for HEAD
 *
 * Revision 1.37  2010/10/26 23:49:12  vralev
 * Issue number:  337
 * Obtained from: vralev
 *
 * Revision 1.36  2010/10/13 15:26:59  deruelle_jean
 * Fix for TCP calls under load freeze JAIN SIP with TCP_POST_PARSING_THREAD_POOL_SIZE > 0
 *
 * Issue number:
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:
 *
 * Revision 1.35  2010/10/12 18:47:16  deruelle_jean
 * Fix for restarting the stack when the gov.nist.javax.sip.TCP_POST_PARSING_THREAD_POOL_SIZE option is used
 *
 * Issue number:
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:
 *
 * Revision 1.34  2010/10/07 15:40:25  deruelle_jean
 * Adding some cleaning up to POST_PARSING_THREAD_POOL option
 *
 * Issue number:
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:
 *
 * Revision 1.33  2010/10/07 15:03:49  deruelle_jean
 * Fixing a deadlock on one post_parser_thread_pool option when there is only 1 thread and message ordering on multiple threads + adding non regression test case
 *
 * Issue number:
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:
 *
 * Revision 1.32  2010/08/19 19:18:01  deruelle_jean
 * Fixing Message Order, there could be race conditions on TCP with multiple threads the order should be maintained
 *
 * Issue number:  301
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:
 *
 * Revision 1.31  2010/07/01 18:53:25  vralev
 * Issue number:  301
 * Obtained from: vralev
 * Submitted by:  vralev
 * Reviewed by:   ranga
 *
 * Handle better conflicting multiple settings. Taking into account only the first one without creating unused threadpools for other settings. In trunk.
 *
 * Revision 1.30  2010/07/01 18:22:56  vralev
 * Issue number:  301
 * Obtained from: vralev
 * Submitted by:  vralev
 * Reviewed by:   ranga
 *
 * Revision 1.29  2010/05/06 14:07:45  deruelle_jean
 * Big update to improve performance by 50% in some cases, TCK + testsuite (cc-buildloop) green, Mobicents Sip Servlets TCK + testsuite green as well
 *
 * Issue number:
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:
 *
 * Revision 1.28  2010/03/19 17:29:46  deruelle_jean
 * Adding getters and setters for the new factories
 *
 * Issue number:
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:
 *
 * Revision 1.27  2010/03/15 17:08:57  deruelle_jean
 * Adding javadoc
 *
 * Issue number:  251
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:   Ranga
 *
 * Revision 1.26  2010/03/15 17:01:21  deruelle_jean
 * Applying patch allowing pluggable message parser implementation
 *
 * Issue number:  251
 * Obtained from:
 * Submitted by:  Jean Deruelle
 * Reviewed by:   Ranga
 *
 * Revision 1.25  2010/02/27 17:34:04  mranga
 * Issue number:  269
 * Fix PipelinedMessageParser.java to use UTF-8 encoding when reading stream.
 *
 * Revision 1.24  2010/02/27 06:09:00  mranga
 * Patch from Frederic
 *
 * Revision 1.23  2009/08/16 17:28:28  mranga
 * Issue number:  208
 * Obtained from:
 * Submitted by:
 * Reviewed by:
 *
 * Add authentication mechanism that uses H(username:domain:password)
 *
 * Revision 1.22  2009/07/17 18:58:02  emcho
 * Converts indentation tabs to spaces so that we have a uniform indentation policy in the whole project.
 *
 * Revision 1.21  2008/05/24 04:10:01  mranga
 *
 * Issue number:   158
 * Obtained from:
 * Submitted by:
 * Reviewed by:   mranga
 *
 * Deliver tx timeout for Canceled INVITE. Fix pipeline thread exit.
 *
 * Revision 1.20  2008/05/22 19:38:07  jbemmel
 * Fix for issue 149: the logic wasn't always closing the internal socket pipe,
 * causing the pipe reader thread to block indefinitely
 *
 * Repeatedly starting/stopping the stack then gives hanging threads
 * Revision 1.19 2007/01/28 13:06:21 mranga
 * Issue number: 99 Obtained from: Submitted by: Reviewed by: mranga
 *
 * Fixed PRACK handling null pointer exception (for proxy case) and cleanup of
 * unused variables.
 *
 * CVS: ----------------------------------------------------------------------
 * CVS: Issue number: CVS: If this change addresses one or more issues, CVS:
 * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change
 * has been taken from another system, CVS: then name the system in this line,
 * otherwise delete it. CVS: Submitted by: CVS: If this code has been
 * contributed to the project by someone else; i.e., CVS: they sent us a patch
 * or a set of diffs, then include their name/email CVS: address here. If this
 * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing
 * pre-commit code reviews and someone else has CVS: reviewed your changes,
 * include their name(s) here. CVS: If you have not had it reviewed then delete
 * this line.
 *
 * Revision 1.18 2006/07/13 09:02:10 mranga Issue number: Obtained from:
 * Submitted by: jeroen van bemmel Reviewed by: mranga Moved some changes from
 * jain-sip-1.2 to java.net
 *
 * CVS: ----------------------------------------------------------------------
 * CVS: Issue number: CVS: If this change addresses one or more issues, CVS:
 * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change
 * has been taken from another system, CVS: then name the system in this line,
 * otherwise delete it. CVS: Submitted by: CVS: If this code has been
 * contributed to the project by someone else; i.e., CVS: they sent us a patch
 * or a set of diffs, then include their name/email CVS: address here. If this
 * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing
 * pre-commit code reviews and someone else has CVS: reviewed your changes,
 * include their name(s) here. CVS: If you have not had it reviewed then delete
 * this line.
 *
 * Revision 1.4 2006/06/19 06:47:27 mranga javadoc fixups
 *
 * Revision 1.3 2006/06/17 10:18:14 mranga Added some synchronization to the
 * sequence number checking. Small javadoc fixups
 *
 * Revision 1.2 2006/06/16 15:26:28 mranga Added NIST disclaimer to all public
 * domain files. Clean up some javadoc. Fixed a leak
 *
 * Revision 1.1.1.1 2005/10/04 17:12:35 mranga
 *
 * Import
 *
 *
 * Revision 1.16 2004/11/30 23:28:14 mranga Issue number: 44 Submitted by: Rob
 * Daugherty Reviewed by: M. Ranganathan
 *
 * TCP Pipelining truncates content when other end of pipe is closed.
 *
 * Revision 1.15 2004/05/30 18:55:56 mranga Reviewed by: mranga Move to timers
 * and eliminate the Transaction scanner Thread to improve scalability and
 * reduce cpu usage.
 *
 * Revision 1.14 2004/05/16 14:13:22 mranga Reviewed by: mranga Fixed the
 * use-count issue reported by Peter Parnes. Added property to prevent against
 * content-length dos attacks.
 *
 * Revision 1.13 2004/03/19 04:22:22 mranga Reviewed by: mranga Added IO Pacing
 * for long writes - split write into chunks and flush after each chunk to avoid
 * socket back pressure.
 *
 * Revision 1.12 2004/03/18 22:01:19 mranga Reviewed by: mranga Get rid of the
 * PipedInputStream from pipelined parser to avoid a copy.
 *
 * Revision 1.11 2004/03/07 22:25:23 mranga Reviewed by: mranga Added a new
 * configuration parameter that instructs the stack to drop a server connection
 * after server transaction termination set
 * gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS=false for this Default behavior
 * is true.
 *
 * Revision 1.10 2004/02/29 15:32:58 mranga Reviewed by: mranga bug fixes on
 * limiting the max message size.
 *
 * Revision 1.9 2004/02/29 00:46:34 mranga Reviewed by: mranga Added new
 * configuration property to limit max message size for TCP transport. The
 * property is gov.nist.javax.sip.MAX_MESSAGE_SIZE
 *
 * Revision 1.8 2004/02/25 21:43:03 mranga Reviewed by: mranga Added a couple of
 * todo's and removed some debug printlns that could slow code down by a bit.
 *
 * Revision 1.7 2004/02/25 20:52:46 mranga Reviewed by: mranga Fix TCP transport
 * so messages in excess of 8192 bytes are accepted.
 *
 * Revision 1.6 2004/01/22 18:39:41 mranga Reviewed by: M. Ranganathan Moved the
 * ifdef SIMULATION and associated tags to the first column so Prep preprocessor
 * can deal with them.
 *
 * Revision 1.5 2004/01/22 14:23:45 mranga Reviewed by: mranga Fixed some minor
 * formatting issues.
 *
 * Revision 1.4 2004/01/22 13:26:31 sverker Issue number: Obtained from:
 * Submitted by: sverker Reviewed by: mranga
 *
 * Major reformat of code to conform with style guide. Resolved compiler and
 * javadoc warnings. Added CVS tags.
 *
 * CVS: ----------------------------------------------------------------------
 * CVS: Issue number: CVS: If this change addresses one or more issues, CVS:
 * then enter the issue number(s) here. CVS: Obtained from: CVS: If this change
 * has been taken from another system, CVS: then name the system in this line,
 * otherwise delete it. CVS: Submitted by: CVS: If this code has been
 * contributed to the project by someone else; i.e., CVS: they sent us a patch
 * or a set of diffs, then include their name/email CVS: address here. If this
 * is your work then delete this line. CVS: Reviewed by: CVS: If we are doing
 * pre-commit code reviews and someone else has CVS: reviewed your changes,
 * include their name(s) here. CVS: If you have not had it reviewed then delete
 * this line.
 *
 */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy