/// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="stack">Reference to SIP stack.</param>
 /// <param name="flow">SIP data flow.</param>
 /// <param name="request">Recieved request.</param>
 /// <param name="transaction">SIP server transaction which must be used to send response back to request maker.</param>
 internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request, SIP_ServerTransaction transaction)
 {
     m_pStack       = stack;
     m_pFlow        = flow;
     m_pRequest     = request;
     m_pTransaction = transaction;
 }
Beispiel #2
0
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="transaction">Server transaction.</param>
        /// <param name="response">SIP response.</param>
        /// <exception cref="ArgumentNullException">Is raised when any of the arguments is null.</exception>
        public SIP_ResponseSentEventArgs(SIP_ServerTransaction transaction, SIP_Response response)
        {
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }
            if (response == null)
            {
                throw new ArgumentNullException("response");
            }

            m_pTransaction = transaction;
            m_pResponse    = response;
        }
        /// <summary>
        /// Matches SIP request to server transaction. If not matching transaction found, returns null.
        /// </summary>
        /// <param name="request">SIP request to match.</param>
        /// <returns>Returns matching transaction or null if no match.</returns>
        internal SIP_ServerTransaction MatchServerTransaction(SIP_Request request)
        {
            /* RFC 3261 17.2.3 Matching Requests to Server Transactions.
             *  This matching rule applies to both INVITE and non-INVITE transactions.
             *
             *  1. the branch parameter in the request is equal to the one in the top Via header
             *     field of the request that created the transaction, and
             *
             *  2. the sent-by value in the top Via of the request is equal to the
             *     one in the request that created the transaction, and
             *
             *  3. the method of the request matches the one that created the transaction, except
             *     for ACK, where the method of the request that created the transaction is INVITE.
             *
             *  Internal implementation notes:
             *      Inernally we use branch + '-' + sent-by for non-CANCEL and for CANCEL
             *      branch + '-' + sent-by + '-' CANCEL. This is because method matching is actually
             *      needed for CANCEL only (CANCEL shares cancelable transaction branch ID).
             */

            SIP_ServerTransaction retVal = null;

            /*
             *  We use branch and sent-by as indexing key for transaction, the only special what we need to
             *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
             *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
             *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
             */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                key += "-CANCEL";
            }

            lock (m_pServerTransactions){
                m_pServerTransactions.TryGetValue(key, out retVal);
            }

            // Don't match ACK for terminated transaction, in that case ACK must be passed to "core".
            if (retVal != null && request.RequestLine.Method == SIP_Methods.ACK && retVal.State == SIP_TransactionState.Terminated)
            {
                retVal = null;
            }

            return(retVal);
        }
        /// <summary>
        /// Ensures that specified request has matching server transaction. If server transaction doesn't exist,
        /// it will be created, otherwise existing transaction will be returned.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns matching transaction.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null.</exception>
        /// <exception cref="InvalidOperationException">Is raised when request.Method is ACK request.</exception>
        public SIP_ServerTransaction EnsureServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }
            if (request.RequestLine.Method == SIP_Methods.ACK)
            {
                throw new InvalidOperationException("ACK request is transaction less request, can't create transaction for it.");
            }

            /*
             *  We use branch and sent-by as indexing key for transaction, the only special what we need to
             *  do is to handle CANCEL, because it has same branch as transaction to be canceled.
             *  For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key.
             *  ACK has also same branch, but we won't do transaction for ACK, so it isn't problem.
             */
            string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy;

            if (request.RequestLine.Method == SIP_Methods.CANCEL)
            {
                key += "-CANCEL";
            }

            lock (m_pServerTransactions){
                SIP_ServerTransaction retVal = null;
                m_pServerTransactions.TryGetValue(key, out retVal);
                // We don't have transaction, create it.
                if (retVal == null)
                {
                    retVal = CreateServerTransaction(flow, request);
                }

                return(retVal);
            }
        }
        /// <summary>
        /// Matches CANCEL requst to SIP server non-CANCEL transaction. Returns null if no match.
        /// </summary>
        /// <param name="cancelRequest">SIP CANCEL request.</param>
        /// <returns>Returns CANCEL matching server transaction or null if no match.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>cancelTransaction</b> is null.</exception>
        /// <exception cref="ArgumentException">Is raised when <b>cancelTransaction</b> has invalid.</exception>
        public SIP_ServerTransaction MatchCancelToTransaction(SIP_Request cancelRequest)
        {
            if (cancelRequest == null)
            {
                throw new ArgumentNullException("cancelRequest");
            }
            if (cancelRequest.RequestLine.Method != SIP_Methods.CANCEL)
            {
                throw new ArgumentException("Argument 'cancelRequest' is not SIP CANCEL request.");
            }

            SIP_ServerTransaction retVal = null;

            // NOTE: There we don't add '-CANCEL' because we want to get CANCEL matching transaction, not CANCEL
            //       transaction itself.
            string key = cancelRequest.Via.GetTopMostValue().Branch + '-' + cancelRequest.Via.GetTopMostValue().SentBy;

            lock (m_pServerTransactions){
                m_pServerTransactions.TryGetValue(key, out retVal);
            }

            return(retVal);
        }
        /// <summary>
        /// Creates new SIP server transaction for specified request.
        /// </summary>
        /// <param name="flow">SIP data flow which is used to receive request.</param>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns added server transaction.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>flow</b> or <b>request</b> is null reference.</exception>
        public SIP_ServerTransaction CreateServerTransaction(SIP_Flow flow, SIP_Request request)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if (flow == null)
            {
                throw new ArgumentNullException("flow");
            }
            if (request == null)
            {
                throw new ArgumentNullException("request");
            }

            lock (m_pServerTransactions){
                SIP_ServerTransaction transaction = new SIP_ServerTransaction(m_pStack, flow, request);
                m_pServerTransactions.Add(transaction.Key, transaction);
                transaction.StateChanged += new EventHandler(delegate(object s, EventArgs e){
                    if (transaction.State == SIP_TransactionState.Terminated)
                    {
                        lock (m_pClientTransactions){
                            m_pServerTransactions.Remove(transaction.Key);
                        }
                    }
                });

                SIP_Dialog dialog = MatchDialog(request);
                if (dialog != null)
                {
                    dialog.AddTransaction(transaction);
                }

                return(transaction);
            }
        }