18#define RESIPROCATE_SUBSYSTEM Subsystem::DUM
23 mOnNewSubscriptionCalled(mEventType ==
"refer"),
26 mLastSubSecs(
Timer::getTimeSecs()),
29 mHaveQueuedRefresh(false),
30 mQueuedRefreshInterval(-1),
33 DebugLog (<<
"ClientSubscription::ClientSubscription from " << request.
brief() <<
": " <<
this);
34 if(request.
method() == SUBSCRIBE)
48 mDialog.mClientSubscriptions.remove(
this);
50 while (!mQueuedNotifies.empty())
52 delete mQueuedNotifies.front();
53 mQueuedNotifies.pop_front();
57 DebugLog(<<
"ClientSubscription::~ClientSubscription: " <<
this);
67ClientSubscription::dispatch(
const SipMessage& msg)
97 mOnNewSubscriptionCalled =
true;
103 bool outOfOrder = mLargestNotifyCSeq > msg.
header(h_CSeq).sequence();
106 mLargestNotifyCSeq = msg.
header(h_CSeq).sequence();
108 if (msg.
exists(h_Contacts))
115 DebugLog(<<
"received out of order notify");
118 mQueuedNotifies.push_back(
new QueuedNotify(msg, outOfOrder));
119 if (mQueuedNotifies.size() == 1)
132 DebugLog(<<
"processing client subscription response");
133 processResponse(msg);
138ClientSubscription::processResponse(
const SipMessage& msg)
146 if (statusCode >= 200 && statusCode <300)
149 if (msg.
exists(h_Expires))
154 if (expires < lastExpires)
160 if(!mOnNewSubscriptionCalled)
162 mOnNewSubscriptionCalled =
true;
177 sendQueuedRefreshRequest();
182 msg.
header(h_To).exists(p_tag) &&
183 msg.
exists(h_Expires) && msg.
header(h_Expires).value() > 0)
185 InfoLog (<<
"Received 481 to SUBSCRIBE, reSUBSCRIBEing (subscription server probably restarted) "
192 (statusCode == 408 ||
194 ((statusCode == 413 ||
200 statusCode == 603) &&
201 msg.
exists(h_RetryAfter))))
205 if(msg.
exists(h_RetryAfter))
207 retryAfter = msg.
header(h_RetryAfter).value();
210 InfoLog (<<
"Received " << statusCode <<
" to SUBSCRIBE "
216 DebugLog(<<
"Application requested failure on Retry-After");
224 DebugLog(<<
"Application requested immediate retry on Retry-After");
226 if (mOnNewSubscriptionCalled)
252 && msg.
exists(h_MinExpires))
267ClientSubscription::processNextNotify()
273 if (mQueuedNotifies.empty())
278 QueuedNotify* qn = mQueuedNotifies.front();
282 unsigned long refreshInterval = 0;
283 bool setRefreshTimer=
false;
284 if (!qn->outOfOrder())
288 if (qn->notify().exists(h_SubscriptionState) && qn->notify().header(h_SubscriptionState).exists(p_expires))
290 expires = qn->notify().header(h_SubscriptionState).param(p_expires);
308 DebugLog(<<
"No expires header in last request, set to " << expires);
312 if(!qn->notify().exists(h_SubscriptionState) ||
319 if (mNextRefreshSecs == 0 || now + refreshInterval < mNextRefreshSecs)
321 mNextRefreshSecs = now + refreshInterval;
322 setRefreshTimer =
true;
328 if (!qn->notify().exists(h_SubscriptionState))
330 if (qn->notify().exists(h_Event) && qn->notify().header(h_Event).value() ==
"refer")
332 SipFrag* frag =
dynamic_cast<SipFrag*
>(qn->notify().getContents());
382 scheduleRefresh(refreshInterval);
391 scheduleRefresh(refreshInterval);
399 isEqualNoCase(qn->notify().header(h_SubscriptionState).param(p_reason),
"timeout"))
402 if(mNextRefreshSecs==0)
408 acceptUpdate(200,
"I just sent a refresh, what more do you want from me?");
417 acceptUpdate(200,
"You terminated my subscription early! What gives?");
426 DebugLog (<<
"[ClientSubscription] " <<
mLastRequest->header(h_To) <<
"[ClientSubscription] Terminated");
434 scheduleRefresh(refreshInterval);
453 InfoLog(<<
"[ClientSubscription] received NOTIFY when subscription was ended, rejecting it...");
459ClientSubscription::dispatch(
const DumTimeout& timer)
468 InfoLog(<<
"ClientSubscription: received NOTIFY timeout when trying to end, terminating...");
490 if (mOnNewSubscriptionCalled)
492 InfoLog(<<
"ClientSubscription: application retry refresh");
497 InfoLog(<<
"ClientSubscription: application retry new request");
510 DebugLog(<<
"got DumTimeout::SendNextNotify");
522 DebugLog(<<
"queue up refresh request");
523 mHaveQueuedRefresh =
true;
524 mQueuedRefreshInterval = expires;
535 mNextRefreshSecs = 0;
552 : mClientSubscriptionHandle(clientSubscriptionHandle),
560 if(mClientSubscriptionHandle.isValid())
562 mClientSubscriptionHandle->requestRefresh(mExpires);
568 return strm <<
"ClientSubscriptionRefreshCommand";
592 if(!immediate && mSubscribed)
622 :mClientSubscriptionHandle(clientSubscriptionHandle), mImmediate(immediate)
629 if(mClientSubscriptionHandle.isValid())
631 mClientSubscriptionHandle->end(mImmediate);
637 return strm <<
"ClientSubscriptionEndCommand";
654 if (mQueuedNotifies.empty())
656 InfoLog(<<
"No queued notify to accept");
660 QueuedNotify* qn = mQueuedNotifies.front();
661 mQueuedNotifies.pop_front();
662 mDustbin.push_back(qn);
675 : mClientSubscriptionHandle(clientSubscriptionHandle),
676 mStatusCode(statusCode),
677 mReason(reason ?
Data(reason) :
Data::Empty)
684 if(mClientSubscriptionHandle.isValid())
686 mClientSubscriptionHandle->acceptUpdate(mStatusCode, mReason.c_str());
692 return strm <<
"ClientSubscriptionAcceptUpdateCommand";
724 if (!mQueuedNotifies.empty() && msg->isResponse())
741 if (mQueuedNotifies.empty())
743 InfoLog(<<
"No queued notify to reject");
747 QueuedNotify* qn = mQueuedNotifies.front();
748 mQueuedNotifies.pop_front();
749 mDustbin.push_back(qn);
752 if (!reasonPhrase.
empty())
765 throw UsageUseException(
"Not a reasonable code to reject a NOTIFY with inside an established dialog.",
787 : mClientSubscriptionHandle(clientSubscriptionHandle),
788 mStatusCode(statusCode),
789 mReasonPhrase(reasonPhrase)
795 if(mClientSubscriptionHandle.isValid())
797 mClientSubscriptionHandle->rejectUpdate(mStatusCode, mReasonPhrase);
803 return strm <<
"ClientSubscriptionRejectUpdateCommand";
820 strm <<
"ClientSubscription " <<
mLastRequest->header(h_From).uri();
842ClientSubscription::sendQueuedRefreshRequest()
846 if (mHaveQueuedRefresh)
848 DebugLog(<<
"send queued refresh request");
849 mHaveQueuedRefresh =
false;
855ClientSubscription::clearDustbin()
857 for (Dustbin::iterator it = mDustbin.begin(); it != mDustbin.end(); ++it)
867ClientSubscription::scheduleRefresh(
unsigned long refreshInterval)
869 if(mNextRefreshSecs-mLastSubSecs < 2)
881 WarningLog(<<
"Server is using an unacceptably short expiry. "
882 "Letting the subscription end so we don't get in a"
883 " tight SUB/NOT loop.");
889 InfoLog (<<
"[ClientSubscription] reSUBSCRIBE in " << refreshInterval);
#define WarningLog(args_)
#define resip_assert(x)
This file needs to work for both C and C++ code.
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
ClientSubscriptionAcceptUpdateCommand(const ClientSubscriptionHandle &clientSubscriptionHandle, int statusCode, const char *reason)
void executeCommand() override
void executeCommand() override
ClientSubscriptionEndCommand(const ClientSubscriptionHandle &clientSubscriptionHandle, bool immediate)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
ClientSubscriptionRefreshCommand(const ClientSubscriptionHandle &clientSubscriptionHandle, uint32_t expires)
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
void executeCommand() override
EncodeStream & encodeBrief(EncodeStream &strm) const override
output a brief description to stream
ClientSubscriptionRejectUpdateCommand(const ClientSubscriptionHandle &clientSubscriptionHandle, int statusCode, const Data &reasonPhrase)
void executeCommand() override
dcm! – update contact in dialog if required
std::shared_ptr< SipMessage > mLastResponse
const Data & getEventType() const noexcept
const Data & getId() const noexcept
std::shared_ptr< SipMessage > mLastRequest
BaseUsageHandle getBaseHandle() const
DialogUsageManager & mDum
virtual void onUpdatePending(ClientSubscriptionHandle, const SipMessage ¬ify, bool outOfOrder)=0
virtual void onFlowTerminated(ClientSubscriptionHandle)
Called when a TCP or TLS flow to the server has terminated.
virtual void onUpdateExtension(ClientSubscriptionHandle, const SipMessage ¬ify, bool outOfOrder)=0
virtual void onNewSubscription(ClientSubscriptionHandle, const SipMessage ¬ify)=0
virtual void onReadyToSend(ClientSubscriptionHandle, SipMessage &msg)
called to allow app to adorn a message.
virtual void onUpdateActive(ClientSubscriptionHandle, const SipMessage ¬ify, bool outOfOrder)=0
virtual void onTerminated(ClientSubscriptionHandle, const SipMessage *msg)=0
virtual void onNotifyNotReceived(ClientSubscriptionHandle)
virtual int onRequestRetry(ClientSubscriptionHandle, int retrySeconds, const SipMessage ¬ify)=0
void acceptUpdate(int statusCode=200, const char *reason=nullptr)
virtual void reSubscribe()
virtual ~ClientSubscription()
void requestRefreshCommand(uint32_t expires=0)
virtual void onReadyToSend(SipMessage &msg) override
EncodeStream & dump(EncodeStream &strm) const override
void rejectUpdateCommand(int statusCode=400, const Data &reasonPhrase=Data::Empty)
virtual void flowTerminated()
Handle< ClientSubscription > ClientSubscriptionHandle
void acceptUpdateCommand(int statusCode=200, const char *reason=nullptr)
Provide asynchronous method access by using command.
void requestRefresh(uint32_t expires=0)
virtual void send(std::shared_ptr< SipMessage > msg) override
virtual void endCommand(bool immediate=false)
void rejectUpdate(int statusCode=400, const Data &reasonPhrase=Data::Empty)
ClientSubscription(DialogUsageManager &dum, Dialog &dialog, const SipMessage &request)
ClientSubscriptionHandle getHandle()
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
bool empty() const noexcept
Checks whether the Data is empty.
void send(std::shared_ptr< SipMessage > request)
ClientSubscriptionHandler * getClientSubscriptionHandler(const Data &eventType)
std::shared_ptr< SipMessage > makeSubscription(const NameAddr &target, const std::shared_ptr< UserProfile > &userProfile, const Data &eventType, AppDialogSet *=nullptr)
std::shared_ptr< UserProfile > getUserProfile() const
AppDialogSetHandle getAppDialogSet() const
virtual void send(std::shared_ptr< SipMessage > msg)
dcm! – kill typedef std::list<DialogId> DialogIdSet;
void makeRequest(SipMessage &request, MethodTypes method, bool incrementCSeq=true)
void makeResponse(SipMessage &response, const SipMessage &request, int responseCode)
static T aBitSmallerThan(T secs)
Used by Registration, Publication and Subscription refreshes, to calculate the time at which a refres...
static FailureMessageEffect determineFailureMessageEffect(const SipMessage &response, const std::set< int > *additionalTransactionTerminatingResponses=NULL)
virtual Brief brief() const
return a facet for brief encoding of message
Represents the "name-addr" and the "addr-spec" elements in the RFC 3261 grammar.
void remove(const Param< NameAddr > ¶mType)
MethodTypes getMethod() const
SIP body type for holding SipFrag contents (MIME content-type message/sipfrag).
Represents a SIP message.
bool isRequest() const
Returns true if message is a request, false otherwise.
const RequestLine & header(const RequestLineType &l) const
Returns the RequestLine. This is only valid for request messages.
bool isResponse() const
Returns true if message is a response, false otherwise.
bool exists(const HeaderBase &headerType) const
Returns true if the given header field is present, false otherwise.
bool isFromWire() const
Check if SipMessage came off the wire.
resip::MethodTypes method() const
returns the method type of the message
static const char * Terminated
static const char * Pending
static const char * Active
This class is used to get the current system time.
static uint64_t getTimeSecs()
Returns the current clock time in seconds.
void post(Message *msg)
Posts a Message to this TransactionUser's fifo.
dcm! – add UnusedChecking(_enum) below;
StatusLineType h_StatusLine
bool isEqualNoCase(const Data &left, const Data &right)
RequestLineType h_RequestLine
unsigned __int64 uint64_t