/// <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; }
/// <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); } }