reSIProcate/DialogUsageManager  9694
Classes | Public Types | Public Member Functions | Protected Member Functions | Private Types | Private Member Functions | Private Attributes | Friends
resip::ClientSubscription Class Reference

dcm! -- update contact in dialog if required More...

#include <ClientSubscription.hxx>

Inheritance diagram for resip::ClientSubscription:
Inheritance graph
[legend]
Collaboration diagram for resip::ClientSubscription:
Collaboration graph
[legend]

List of all members.

Classes

class  QueuedNotify

Public Types

typedef Handle
< ClientSubscription
ClientSubscriptionHandle

Public Member Functions

 ClientSubscription (DialogUsageManager &dum, Dialog &dialog, const SipMessage &request, UInt32 defaultSubExpiration)
ClientSubscriptionHandle getHandle ()
void acceptUpdate (int statusCode=200, const char *reason=0)
void rejectUpdate (int statusCode=400, const Data &reasonPhrase=Data::Empty)
void requestRefresh (UInt32 expires=0)
virtual void end ()
void end (bool immediate)
virtual void reSubscribe ()
void acceptUpdateCommand (int statusCode=200, const char *reason=0)
 Provide asynchronous method access by using command.
void rejectUpdateCommand (int statusCode=400, const Data &reasonPhrase=Data::Empty)
void requestRefreshCommand (UInt32 expires=0)
virtual void endCommand (bool immediate=false)
virtual EncodeStreamdump (EncodeStream &strm) const

Protected Member Functions

virtual ~ClientSubscription ()
virtual void dialogDestroyed (const SipMessage &msg)
virtual void onReadyToSend (SipMessage &msg)
virtual void send (SharedPtr< SipMessage > msg)
virtual void flowTerminated ()

Private Types

typedef std::deque
< QueuedNotify * > 
NotifyQueue
typedef std::vector
< QueuedNotify * > 
Dustbin

Private Member Functions

virtual void dispatch (const SipMessage &msg)
virtual void dispatch (const DumTimeout &timer)
void sendQueuedRefreshRequest ()
void processNextNotify ()
void processResponse (const SipMessage &response)
void clearDustbin ()
void scheduleRefresh (unsigned long refreshInterval)
 ClientSubscription (const ClientSubscription &)
ClientSubscriptionoperator= (const ClientSubscription &)

Private Attributes

NotifyQueue mQueuedNotifies
Dustbin mDustbin
bool mOnNewSubscriptionCalled
bool mEnded
UInt64 mNextRefreshSecs
UInt64 mLastSubSecs
UInt32 mDefaultExpires
bool mRefreshing
bool mHaveQueuedRefresh
int mQueuedRefreshInterval
unsigned int mLargestNotifyCSeq

Friends

class Dialog
class InviteSession

Detailed Description

dcm! -- update contact in dialog if required

Definition at line 14 of file ClientSubscription.hxx.


Member Typedef Documentation

Definition at line 20 of file ClientSubscription.hxx.

typedef std::vector<QueuedNotify*> resip::ClientSubscription::Dustbin [private]

Definition at line 68 of file ClientSubscription.hxx.

typedef std::deque<QueuedNotify*> resip::ClientSubscription::NotifyQueue [private]

Definition at line 65 of file ClientSubscription.hxx.


Constructor & Destructor Documentation

ClientSubscription::ClientSubscription ( DialogUsageManager dum,
Dialog dialog,
const SipMessage request,
UInt32  defaultSubExpiration 
)

Definition at line 21 of file ClientSubscription.cxx.

References resip::Message::brief(), DebugLog, resip::Dialog::makeRequest(), resip::DialogUsage::mDialog, resip::SipMessage::method(), and resip::BaseSubscription::mLastRequest.

   : BaseSubscription(dum, dialog, request),
     mOnNewSubscriptionCalled(mEventType == "refer"),  // don't call onNewSubscription for Refer subscriptions
     mEnded(false),
     mNextRefreshSecs(0),
     mLastSubSecs(Timer::getTimeSecs()), // Not exactly, but more forgiving
     mDefaultExpires(defaultSubExpiration),
     mRefreshing(false),
     mHaveQueuedRefresh(false),
     mQueuedRefreshInterval(-1),
     mLargestNotifyCSeq(0)
{
   DebugLog (<< "ClientSubscription::ClientSubscription from " << request.brief());   
   if(request.method() == SUBSCRIBE)
   {
      *mLastRequest = request;
      if (defaultSubExpiration > 0)
      {
         mLastRequest->header(h_Expires).value() = defaultSubExpiration;
      }
   }
   else
   {
      // If a NOTIFY request is use to make this ClientSubscription, then create the implied SUBSCRIBE 
      // request as the mLastRequest
      mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
   }
}

Here is the call graph for this function:

ClientSubscription::~ClientSubscription ( ) [protected, virtual]

Definition at line 51 of file ClientSubscription.cxx.

References clearDustbin(), resip::Dialog::mClientSubscriptions, resip::DialogUsage::mDialog, and mQueuedNotifies.

{
   mDialog.mClientSubscriptions.remove(this);

   while (!mQueuedNotifies.empty())
   {
      delete mQueuedNotifies.front();
      mQueuedNotifies.pop_front();
   }

   clearDustbin();
}

Here is the call graph for this function:

resip::ClientSubscription::ClientSubscription ( const ClientSubscription ) [private]

Member Function Documentation

void ClientSubscription::acceptUpdate ( int  statusCode = 200,
const char *  reason = 0 
)

Definition at line 616 of file ClientSubscription.cxx.

References h_StatusLine, InfoLog, resip::Dialog::makeResponse(), resip::DialogUsage::mDialog, mDustbin, resip::BaseSubscription::mLastResponse, mQueuedNotifies, resip::ClientSubscription::QueuedNotify::notify(), and send().

Referenced by processNextNotify().

{
   assert(!mQueuedNotifies.empty());
   if (mQueuedNotifies.empty())
   {
      InfoLog(<< "No queued notify to accept");
      return;
   }

   QueuedNotify* qn = mQueuedNotifies.front();
   mQueuedNotifies.pop_front();
   mDustbin.push_back(qn);
   mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
   if(reason)
   {
      mLastResponse->header(h_StatusLine).reason()=reason;
   }
   send(mLastResponse);
}

Here is the call graph for this function:

void ClientSubscription::acceptUpdateCommand ( int  statusCode = 200,
const char *  reason = 0 
)

Provide asynchronous method access by using command.

Definition at line 663 of file ClientSubscription.cxx.

References resip::BaseUsage::mDum, and resip::TransactionUser::post().

{
   mDum.post(new ClientSubscriptionAcceptUpdateCommand(*this, statusCode, reason));
}

Here is the call graph for this function:

void ClientSubscription::clearDustbin ( ) [private]

Definition at line 817 of file ClientSubscription.cxx.

References mDustbin.

Referenced by dispatch(), and ~ClientSubscription().

{
   for (Dustbin::iterator it = mDustbin.begin(); it != mDustbin.end(); ++it)
   {
      delete *it;
   }

   mDustbin.clear();

}
void ClientSubscription::dialogDestroyed ( const SipMessage msg) [protected, virtual]
void ClientSubscription::dispatch ( const SipMessage msg) [private, virtual]

dcm! -- heavy, should just store enough information to make response

Implements resip::BaseUsage.

Definition at line 71 of file ClientSubscription.cxx.

References resip::Message::brief(), clearDustbin(), DebugLog, resip::SipMessage::exists(), resip::DialogUsage::getAppDialogSet(), resip::DialogUsageManager::getClientSubscriptionHandler(), getHandle(), resip::RequestLine::getMethod(), h_RequestLine, resip::SipMessage::header(), InfoLog, resip::SipMessage::isRequest(), resip::DialogUsage::mDialog, resip::BaseUsage::mDum, resip::BaseSubscription::mEventType, mLargestNotifyCSeq, resip::BaseSubscription::mLastRequest, mOnNewSubscriptionCalled, mQueuedNotifies, mRefreshing, resip::Dialog::mRemoteTarget, processNextNotify(), and processResponse().

Referenced by resip::Dialog::dispatch().

{
   DebugLog (<< "ClientSubscription::dispatch " << msg.brief());
   
   ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
   assert(handler);

   clearDustbin();

   // asserts are checks the correctness of Dialog::dispatch
   if (msg.isRequest() )
   {
      assert( msg.header(h_RequestLine).getMethod() == NOTIFY );
      mRefreshing = false;

      // !dlb! 481 NOTIFY iff state is dead?

      //mLastNotify = msg;

      if (!mOnNewSubscriptionCalled && !getAppDialogSet()->isReUsed())
      {
         InfoLog (<< "[ClientSubscription] " << mLastRequest->header(h_To));
         if (msg.exists(h_Contacts))
         {
            mDialog.mRemoteTarget = msg.header(h_Contacts).front();
         }
         
         handler->onNewSubscription(getHandle(), msg);
         mOnNewSubscriptionCalled = true;
      }         

      bool outOfOrder = mLargestNotifyCSeq > msg.header(h_CSeq).sequence();
      if (!outOfOrder)
      {
         mLargestNotifyCSeq = msg.header(h_CSeq).sequence();
      }
      else
      {
         DebugLog(<< "received out of order notify");
      }

      mQueuedNotifies.push_back(new QueuedNotify(msg, outOfOrder));
      if (mQueuedNotifies.size() == 1)
      {
         DebugLog(<< "no queued notify");
         processNextNotify();
         return;
      }
      else
      {
         DebugLog(<< "Notify gets queued");
      }
   }
   else
   {
      DebugLog(<< "processing client subscription response");
      processResponse(msg);
   }
}

Here is the call graph for this function:

void ClientSubscription::dispatch ( const DumTimeout timer) [private, virtual]

Implements resip::BaseUsage.

Definition at line 447 of file ClientSubscription.cxx.

References DebugLog, resip::DialogUsageManager::getClientSubscriptionHandler(), getHandle(), InfoLog, resip::BaseUsage::mDum, mEnded, resip::BaseSubscription::mEventType, mOnNewSubscriptionCalled, resip::BaseSubscription::mTimerSeq, resip::ClientSubscriptionHandler::onNotifyNotReceived(), resip::ClientSubscriptionHandler::onTerminated(), processNextNotify(), requestRefresh(), reSubscribe(), resip::DumTimeout::SendNextNotify, resip::DumTimeout::seq(), resip::DumTimeout::Subscription, resip::DumTimeout::SubscriptionRetry, resip::DumTimeout::type(), and resip::DumTimeout::WaitForNotify.

{
   if (timer.seq() == mTimerSeq)
   {
      if(timer.type() == DumTimeout::WaitForNotify)
      {
         ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
         if(mOnNewSubscriptionCalled && mEnded)
         {
            // NOTIFY terminated didn't come in
            handler->onTerminated(getHandle(),0);
            delete this;
            return;
         }

         // Initial NOTIFY never came in; let app decide what to do
         handler->onNotifyNotReceived(getHandle());
      }
      else if (timer.type() == DumTimeout::SubscriptionRetry)
      {
         // this indicates that the ClientSubscription was created by a 408
         if (mOnNewSubscriptionCalled)
         {
            InfoLog(<< "ClientSubscription: application retry refresh");
            requestRefresh();
         }
         else
         {
            InfoLog(<< "ClientSubscription: application retry new request");
            reSubscribe();  // will delete "this"
            return;
         }
      }
      else if(timer.type() == DumTimeout::Subscription)
      {
         requestRefresh();
      }
   }
   else if(timer.seq() == 0 && timer.type() == DumTimeout::SendNextNotify)
   {
      DebugLog(<< "got DumTimeout::SendNextNotify");
      processNextNotify();
   }
}

Here is the call graph for this function:

EncodeStream & ClientSubscription::dump ( EncodeStream strm) const [virtual]

Implements resip::BaseUsage.

Definition at line 780 of file ClientSubscription.cxx.

References resip::BaseSubscription::mLastRequest.

{
   strm << "ClientSubscription " << mLastRequest->header(h_From).uri();
   return strm;
}
void ClientSubscription::end ( ) [virtual]

Implements resip::BaseUsage.

Definition at line 555 of file ClientSubscription.cxx.

Referenced by resip::Dialog::end().

{
    end(false /* immediate? */);
}
void ClientSubscription::end ( bool  immediate)

Definition at line 561 of file ClientSubscription.cxx.

References resip::DialogUsageManager::addTimer(), resip::BaseUsage::getBaseHandle(), h_RequestLine, InfoLog, resip::Dialog::makeRequest(), resip::DialogUsage::mDialog, resip::BaseUsage::mDum, mEnded, resip::BaseSubscription::mLastRequest, resip::BaseSubscription::mTimerSeq, send(), resip::Timer::T1, and resip::DumTimeout::WaitForNotify.

{
   InfoLog (<< "End subscription: " << mLastRequest->header(h_RequestLine).uri());

   if (!mEnded)
   {
      if(!immediate)
      {
         mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
         mLastRequest->header(h_Expires).value() = 0;
         mEnded = true;
         send(mLastRequest);
         // Timer for NOTIFY terminated
         mDum.addTimer(DumTimeout::WaitForNotify, 
                 64*Timer::T1, 
                 getBaseHandle(),
                 ++mTimerSeq);
      }
      else
      {
         delete this;
      }
   }
}

Here is the call graph for this function:

void ClientSubscription::endCommand ( bool  immediate = false) [virtual]

Definition at line 610 of file ClientSubscription.cxx.

References resip::BaseUsage::mDum, and resip::TransactionUser::post().

{
   mDum.post(new ClientSubscriptionEndCommand(*this, immediate));
}

Here is the call graph for this function:

void ClientSubscription::flowTerminated ( ) [protected, virtual]
ClientSubscriptionHandle ClientSubscription::getHandle ( )
void ClientSubscription::onReadyToSend ( SipMessage msg) [protected, virtual]
ClientSubscription& resip::ClientSubscription::operator= ( const ClientSubscription ) [private]
void ClientSubscription::processNextNotify ( ) [private]

dcm! There is a timing issue in this code which can cause this to be called when there are no queued NOTIFY messages. Probably a subscription teardown/timer crossover.

fjoanis! Is 481 a proper error code in this case?

Definition at line 253 of file ClientSubscription.cxx.

References resip::Helper::aBitSmallerThan(), acceptUpdate(), resip::Symbols::Active, DebugLog, resip::SipMessage::exists(), resip::DialogUsageManager::getClientSubscriptionHandler(), resip::SipMessage::getContents(), getHandle(), resip::Timer::getTimeSecs(), h_StatusLine, resip::SipMessage::header(), InfoLog, resip::isEqualNoCase(), resip::SipMessage::isResponse(), resip::Dialog::makeResponse(), mDefaultExpires, resip::DialogUsage::mDialog, resip::BaseUsage::mDum, mEnded, resip::SipFrag::message(), resip::BaseSubscription::mEventType, resip::BaseSubscription::mLastRequest, resip::BaseSubscription::mLastResponse, mLastSubSecs, mNextRefreshSecs, mQueuedNotifies, resip::ClientSubscription::QueuedNotify::notify(), resip::ClientSubscriptionHandler::onTerminated(), resip::ClientSubscriptionHandler::onUpdateActive(), resip::ClientSubscriptionHandler::onUpdateExtension(), resip::ClientSubscriptionHandler::onUpdatePending(), resip::ClientSubscription::QueuedNotify::outOfOrder(), resip::Symbols::Pending, rejectUpdate(), scheduleRefresh(), send(), and resip::Symbols::Terminated.

Referenced by dispatch().

{
   //assert(!mQueuedNotifies.empty());
   if (mQueuedNotifies.empty())
   {
      return;
   }

   QueuedNotify* qn = mQueuedNotifies.front();
   ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
   assert(handler);

   unsigned long refreshInterval = 0;
   bool setRefreshTimer=false; 
   if (!qn->outOfOrder())
   {
      UInt32 expires = 0;
      //default to 3600 seconds so non-compliant endpoints don't result in leaked usages
      if (qn->notify().exists(h_SubscriptionState) && qn->notify().header(h_SubscriptionState).exists(p_expires))
      {
         expires = qn->notify().header(h_SubscriptionState).param(p_expires);
      }
      else if (mLastRequest->exists(h_Expires))
      {
         expires = mLastRequest->header(h_Expires).value();
      }
      else if (mDefaultExpires)
      {
         /* if we haven't gotten an expires value from:
            1. the subscription state from this notify
            2. the last request
            then use the default expires (meaning it came from the 2xx in response
            to the initial SUBSCRIBE). .mjf.
          */
         expires = mDefaultExpires;
      }
      else
      {
         expires = 3600;
      }
      
      if (!mLastRequest->exists(h_Expires))
      {
         DebugLog(<< "No expires header in last request, set to " << expires);
         mLastRequest->header(h_Expires).value() = expires;
      }

      if(!qn->notify().exists(h_SubscriptionState) || 
         !isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
      {
         // Don't do this stuff for a NOTIFY terminated.
         UInt64 now = Timer::getTimeSecs();
         refreshInterval = Helper::aBitSmallerThan((signed long)expires);
         
         if (mNextRefreshSecs == 0 || now + refreshInterval < mNextRefreshSecs)
         {
            mNextRefreshSecs = now + refreshInterval;
            setRefreshTimer = true;
         }
      }
   }
   //if no subscription state header, treat as an extension. Only allow for
   //refer to handle non-compliant implementations
   if (!qn->notify().exists(h_SubscriptionState))
   {
      if (qn->notify().exists(h_Event) && qn->notify().header(h_Event).value() == "refer")
      {
         SipFrag* frag  = dynamic_cast<SipFrag*>(qn->notify().getContents());
         if (frag)
         {
            if (frag->message().isResponse())
            {
               int code = frag->message().header(h_StatusLine).statusCode();
               if (code < 200)
               {
                  handler->onUpdateExtension(getHandle(), qn->notify(), qn->outOfOrder());
               }
               else
               {
                  acceptUpdate();
                  mEnded = true;                     
                  handler->onTerminated(getHandle(), &qn->notify());
                  delete this;
               }
            }
            else
            {
               acceptUpdate();
               mEnded = true;
               handler->onTerminated(getHandle(), &qn->notify());
               delete this;
            }
         }
         else
         {
            acceptUpdate();
            mEnded = true;
            handler->onTerminated(getHandle(), &qn->notify());
            delete this;
         }
      }
      else
      {            
         mDialog.makeResponse(*mLastResponse, qn->notify(), 400);
         mLastResponse->header(h_StatusLine).reason() = "Missing Subscription-State header";
         send(mLastResponse);
         mEnded = true;
         handler->onTerminated(getHandle(), &qn->notify());
         delete this;
      }
      return;
   }

   if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Active))
   {
      if (setRefreshTimer)
      {
         scheduleRefresh(refreshInterval);
      }
         
      handler->onUpdateActive(getHandle(), qn->notify(), qn->outOfOrder());
   }
   else if (!mEnded && isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Pending))
   {
      if (setRefreshTimer)
      {
         scheduleRefresh(refreshInterval);
      }

      handler->onUpdatePending(getHandle(), qn->notify(), qn->outOfOrder());
   }
   else if (isEqualNoCase(qn->notify().header(h_SubscriptionState).value(), Symbols::Terminated))
   {
      if(mLastRequest->header(h_Expires).value()!=0 &&
         isEqualNoCase(qn->notify().header(h_SubscriptionState).param(p_reason), "timeout"))
      {
         // Unexpected timeout of some sort. Look closer.
         if(mNextRefreshSecs==0)
         {
            // No refresh scheduled; maybe we are trying to avoid a tight SUB/
            // NOT loop here?
            if(Helper::aBitSmallerThan((signed long)(Timer::getTimeSecs() - mLastSubSecs)) < 2)
            {
               acceptUpdate(200, "I just sent a refresh, what more do you want "
                                 "from me?");
            }
            else
            {
               acceptUpdate(200, "Why didn't I refresh here?");
            }
         }
         else
         {
            acceptUpdate(200, "You terminated my subscription early! What "
                              "gives?");
         }
      }
      else
      {
         acceptUpdate();
      }
      mEnded = true;
      handler->onTerminated(getHandle(), &qn->notify());
      DebugLog (<< "[ClientSubscription] " << mLastRequest->header(h_To) << "[ClientSubscription] Terminated");                   
      delete this;
      return;
   }
   else if (!mEnded)
   {
      handler->onUpdateExtension(getHandle(), qn->notify(), qn->outOfOrder());
   }
   else if (mEnded)
   {
      // We received a NOTIFY message when we thought the subscription was
      // ended. This can happen, for example, when a previously sent NOTIFY gets
      // resent while we (ClientSubscription) are trying to terminate the
      // subscription. If we don't accept/reject this NOTIFY, it will stay into
      // the mQueuedNotifies queue and we'll never terminate the subscription
      // even if the server sends a NOTIFY/terminated. All received NOTIFY would
      // get piled up on mQueuedNotifies and they will never get processed.
      //
      // Note that if that NOTIFY is in fact the terminated one, it will get
      // caught by another if statement above and acted upon appropriately.
      //
      InfoLog(<< "[ClientSubscription] received NOTIFY when subscription was ended, rejecting it...");
      rejectUpdate(481);
   }
}

Here is the call graph for this function:

void ClientSubscription::processResponse ( const SipMessage response) [private]

Definition at line 133 of file ClientSubscription.cxx.

References resip::DialogUsageManager::addTimer(), DebugLog, resip::SipMessage::exists(), resip::BaseUsage::getBaseHandle(), resip::DialogUsageManager::getClientSubscriptionHandler(), getHandle(), resip::SipMessage::getReceivedTransport(), h_StatusLine, resip::SipMessage::header(), InfoLog, resip::BaseUsage::mDum, mEnded, resip::BaseSubscription::mEventType, resip::BaseSubscription::mLastRequest, mOnNewSubscriptionCalled, mRefreshing, resip::BaseSubscription::mTimerSeq, resip::ClientSubscriptionHandler::onRequestRetry(), resip::ClientSubscriptionHandler::onTerminated(), requestRefresh(), reSubscribe(), sendQueuedRefreshRequest(), resip::DumTimeout::SubscriptionRetry, resip::Timer::T1, and resip::DumTimeout::WaitForNotify.

Referenced by dispatch().

{
   ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
   assert(handler);

   mRefreshing = false;
   int statusCode = msg.header(h_StatusLine).statusCode();

   if (statusCode >= 200 && statusCode <300)
   {
      if (msg.exists(h_Expires))
      {
         // grab the expires from the 2xx in case there is not one on the NOTIFY .mjf.
         UInt32 expires = msg.header(h_Expires).value();
         UInt32 lastExpires = mLastRequest->header(h_Expires).value();
         if (expires < lastExpires)
         {
            mLastRequest->header(h_Expires).value() = expires;
         }
      }

      if(!mOnNewSubscriptionCalled)
      {
         // Timer for initial NOTIFY; since we don't know when the initial
         // SUBSRIBE is sent, we have to set the timer when the 200 comes in, if
         // it beat the NOTIFY.
         mDum.addTimer(DumTimeout::WaitForNotify, 
                 64*Timer::T1, 
                 getBaseHandle(),
                 ++mTimerSeq);
      }

      sendQueuedRefreshRequest();
   }
   else if (!mEnded &&
            statusCode == 481 &&
            msg.exists(h_Expires) && msg.header(h_Expires).value() > 0)
   {
      InfoLog (<< "Received 481 to SUBSCRIBE, reSUBSCRIBEing (presence server probably restarted) "
               << mLastRequest->header(h_To));

      reSubscribe();  // will delete "this"
      return;
   }
   else if (!mEnded &&
            (statusCode == 408 ||
             (statusCode == 503 && msg.getReceivedTransport() == 0) ||
             ((statusCode == 413 ||
               statusCode == 480 ||
               statusCode == 486 ||
               statusCode == 500 ||
               statusCode == 503 ||
               statusCode == 600 ||
               statusCode == 603) &&
              msg.exists(h_RetryAfter))))
   {
      int retry;
      int retryAfter = 0;
      if(msg.exists(h_RetryAfter))
      {
         retryAfter = msg.header(h_RetryAfter).value();
      }

      InfoLog (<< "Received " << statusCode << " to SUBSCRIBE "
               << mLastRequest->header(h_To));
      retry = handler->onRequestRetry(getHandle(), retryAfter, msg);

      if (retry < 0)
      {
         DebugLog(<< "Application requested failure on Retry-After");
         mEnded = true;
         handler->onTerminated(getHandle(), &msg);
         delete this;
         return;
      }
      else if (retry == 0)
      {
         DebugLog(<< "Application requested immediate retry on Retry-After");

         if (mOnNewSubscriptionCalled)
         {
            // If we already have a dialog, then just refresh again
            requestRefresh();
         }
         else
         {
            reSubscribe();  // will delete "this"
            return;
         }
      }
      else 
      {
         // leave the usage around until the timeout
         // !dlb! would be nice to set the state to something dead, but not used
         mDum.addTimer(DumTimeout::SubscriptionRetry, 
                       retry, 
                       getBaseHandle(),
                       ++mTimerSeq);
         // leave the usage around until the timeout
         return;
      }            
   }
   else if (msg.header(h_StatusLine).statusCode() >= 300)
   {
      if (msg.header(h_StatusLine).statusCode() == 423 
          && msg.exists(h_MinExpires))
      {
         requestRefresh(msg.header(h_MinExpires).value());            
      }
      else
      {
         mEnded = true;
         handler->onTerminated(getHandle(), &msg);
         delete this;
         return;
      }
   }
}

Here is the call graph for this function:

void ClientSubscription::rejectUpdate ( int  statusCode = 400,
const Data reasonPhrase = Data::Empty 
)

Definition at line 698 of file ClientSubscription.cxx.

References resip::Helper::ApplicationDependant, resip::Helper::determineFailureMessageEffect(), resip::Helper::DialogTermination, resip::Data::empty(), resip::SharedPtr< T >::get(), resip::DialogUsageManager::getClientSubscriptionHandler(), getHandle(), h_StatusLine, InfoLog, resip::Dialog::makeResponse(), resip::DialogUsage::mDialog, resip::BaseUsage::mDum, mDustbin, mEnded, resip::BaseSubscription::mEventType, resip::BaseSubscription::mLastResponse, mQueuedNotifies, resip::ClientSubscription::QueuedNotify::notify(), resip::ClientSubscriptionHandler::onTerminated(), resip::Helper::OptionalRetryAfter, resip::Helper::RetryAfter, send(), resip::Helper::TransactionTermination, and resip::Helper::UsageTermination.

Referenced by processNextNotify().

{
   ClientSubscriptionHandler* handler = mDum.getClientSubscriptionHandler(mEventType);
   assert(handler);   
   assert(!mQueuedNotifies.empty());
   if (mQueuedNotifies.empty())
   {
      InfoLog(<< "No queued notify to reject");
      return;
   }

   QueuedNotify* qn = mQueuedNotifies.front();
   mQueuedNotifies.pop_front();
   mDustbin.push_back(qn);

   mDialog.makeResponse(*mLastResponse, qn->notify(), statusCode);
   if (!reasonPhrase.empty())
   {
      mLastResponse->header(h_StatusLine).reason() = reasonPhrase;
   }
   
   send(mLastResponse);
   switch (Helper::determineFailureMessageEffect(*mLastResponse))
   {
      case Helper::TransactionTermination:
      case Helper::RetryAfter:
         break;            
      case Helper::OptionalRetryAfter:
      case Helper::ApplicationDependant: 
         throw UsageUseException("Not a reasonable code to reject a NOTIFY with inside an established dialog.", 
                                 __FILE__, __LINE__);
         break;            
      case Helper::DialogTermination: //?dcm? -- throw or destroy this?
      case Helper::UsageTermination:
         mEnded = true;
         handler->onTerminated(getHandle(), mLastResponse.get());
         delete this;
         break;
   }
}

Here is the call graph for this function:

void ClientSubscription::rejectUpdateCommand ( int  statusCode = 400,
const Data reasonPhrase = Data::Empty 
)

Definition at line 765 of file ClientSubscription.cxx.

References resip::BaseUsage::mDum, and resip::TransactionUser::post().

{
   mDum.post(new ClientSubscriptionRejectUpdateCommand(*this, statusCode, reasonPhrase));
}

Here is the call graph for this function:

void ClientSubscription::requestRefresh ( UInt32  expires = 0)

dcm! -- need a mechanism to retrieve this for the event package...part of

Definition at line 493 of file ClientSubscription.cxx.

References resip::DialogUsageManager::addTimer(), DebugLog, resip::BaseUsage::getBaseHandle(), resip::Timer::getTimeSecs(), InfoLog, resip::Dialog::makeRequest(), resip::DialogUsage::mDialog, resip::BaseUsage::mDum, mEnded, mHaveQueuedRefresh, resip::BaseSubscription::mLastRequest, mLastSubSecs, mNextRefreshSecs, mQueuedRefreshInterval, mRefreshing, resip::BaseSubscription::mTimerSeq, send(), resip::Timer::T1, and resip::DumTimeout::WaitForNotify.

Referenced by dispatch(), processResponse(), and sendQueuedRefreshRequest().

{
   if (!mEnded)
   {
      if (mRefreshing)
      {
         DebugLog(<< "queue up refresh request");
         mHaveQueuedRefresh = true;
         mQueuedRefreshInterval = expires;
         return;
      }

      mDialog.makeRequest(*mLastRequest, SUBSCRIBE);
      //the map that stores the handlers, or part of the handler API
      if(expires > 0)
      {
         mLastRequest->header(h_Expires).value() = expires;
      }
      mNextRefreshSecs = 0;
      InfoLog (<< "Refresh subscription: " << mLastRequest->header(h_Contacts).front());
      mRefreshing = true;
      mLastSubSecs = Timer::getTimeSecs();
      send(mLastRequest);
      // Timer for reSUB NOTIFY.
      mDum.addTimer(DumTimeout::WaitForNotify, 
              64*Timer::T1, 
              getBaseHandle(),
              ++mTimerSeq);
   }
}

Here is the call graph for this function:

void ClientSubscription::requestRefreshCommand ( UInt32  expires = 0)

Definition at line 549 of file ClientSubscription.cxx.

References resip::BaseUsage::mDum, and resip::TransactionUser::post().

{
   mDum.post(new ClientSubscriptionRefreshCommand(*this, expires));
}

Here is the call graph for this function:

void ClientSubscription::reSubscribe ( ) [virtual]
void ClientSubscription::scheduleRefresh ( unsigned long  refreshInterval) [private]

Definition at line 829 of file ClientSubscription.cxx.

References resip::DialogUsageManager::addTimer(), resip::BaseUsage::getBaseHandle(), InfoLog, resip::BaseUsage::mDum, mLastSubSecs, mNextRefreshSecs, resip::BaseSubscription::mTimerSeq, resip::DumTimeout::Subscription, and WarningLog.

Referenced by processNextNotify().

{
   if(mNextRefreshSecs-mLastSubSecs < 2)
   {
      // Server is using an unreasonably short expiry; we sent a SUB
      // very recently, and the server has told us to refresh almost 
      // immediately. By the time our refresh timer pops, less than two 
      // seconds will have elapsed since our last SUBSCRIBE. This is 
      // unacceptable. Just let the subscription end.
      // It is also possible that our refresh SUB has crossed an update NOTIFY 
      // on the wire; in this case, the right thing to do is to wait until a 
      // NOTIFY for our refresh SUB comes in, which is exactly what this code 
      // ends up doing in this case.
      // ?bwc? Make this minimum inter-SUBSCRIBE time configurable?
      WarningLog(<< "Server is using an unacceptably short expiry. "
                  "Letting the subscription end so we don't get in a"
                  " tight SUB/NOT loop.");
      mNextRefreshSecs=0;
   }
   else
   {
      mDum.addTimer(DumTimeout::Subscription, refreshInterval, getBaseHandle(), ++mTimerSeq);
      InfoLog (<< "[ClientSubscription] reSUBSCRIBE in " << refreshInterval);
   }
}

Here is the call graph for this function:

void ClientSubscription::send ( SharedPtr< SipMessage msg) [protected, virtual]
void ClientSubscription::sendQueuedRefreshRequest ( ) [private]

Definition at line 804 of file ClientSubscription.cxx.

References DebugLog, mHaveQueuedRefresh, mQueuedRefreshInterval, mRefreshing, and requestRefresh().

Referenced by processResponse().

{
   assert(!mRefreshing);

   if (mHaveQueuedRefresh)
   {
      DebugLog(<< "send queued refresh request");
      mHaveQueuedRefresh = false;
      requestRefresh(mQueuedRefreshInterval);
   }
}

Here is the call graph for this function:


Friends And Related Function Documentation

friend class Dialog [friend]

Reimplemented from resip::BaseSubscription.

Definition at line 48 of file ClientSubscription.hxx.

friend class InviteSession [friend]

Definition at line 49 of file ClientSubscription.hxx.


Member Data Documentation

Definition at line 79 of file ClientSubscription.hxx.

Referenced by processNextNotify().

Definition at line 69 of file ClientSubscription.hxx.

Referenced by acceptUpdate(), clearDustbin(), and rejectUpdate().

Definition at line 82 of file ClientSubscription.hxx.

Referenced by requestRefresh(), and sendQueuedRefreshRequest().

Definition at line 85 of file ClientSubscription.hxx.

Referenced by dispatch().

Definition at line 76 of file ClientSubscription.hxx.

Referenced by processNextNotify(), requestRefresh(), and scheduleRefresh().

Definition at line 75 of file ClientSubscription.hxx.

Referenced by processNextNotify(), requestRefresh(), and scheduleRefresh().

Definition at line 71 of file ClientSubscription.hxx.

Referenced by dispatch(), and processResponse().

Definition at line 83 of file ClientSubscription.hxx.

Referenced by requestRefresh(), and sendQueuedRefreshRequest().


The documentation for this class was generated from the following files: