/[resiprocate]/main/resip/dum/ServerSubscription.cxx
ViewVC logotype

Contents of /main/resip/dum/ServerSubscription.cxx

Parent Directory Parent Directory | Revision Log Revision Log


Revision 11213 - (show annotations) (download)
Mon Jun 16 13:46:20 2014 UTC (5 years, 5 months ago) by sgodin
File MIME type: text/plain
File size: 15067 byte(s)
-allow ServerSubscription::end to specify a Retry-After value - thanks to Roman Romanchenko 
1 #include "resip/dum/AppDialog.hxx"
2 #include "resip/dum/Dialog.hxx"
3 #include "resip/dum/DialogUsageManager.hxx"
4 #include "resip/dum/ServerSubscription.hxx"
5 #include "resip/dum/SubscriptionHandler.hxx"
6 #include "resip/dum/UsageUseException.hxx"
7 #include "resip/stack/Helper.hxx"
8 #include "rutil/Logger.hxx"
9
10 #include <time.h>
11
12 using namespace resip;
13
14 #define RESIPROCATE_SUBSYSTEM Subsystem::DUM
15
16 ServerSubscriptionHandle
17 ServerSubscription::getHandle()
18 {
19 return ServerSubscriptionHandle(mDum, getBaseHandle().getId());
20 }
21
22 ServerSubscription::ServerSubscription(DialogUsageManager& dum,
23 Dialog& dialog,
24 const SipMessage& req)
25 : BaseSubscription(dum, dialog, req),
26 mSubscriber(req.header(h_From).uri().getAor()),
27 mExpires(60),
28 mAbsoluteExpiry(0)
29 {
30 if (req.header(h_RequestLine).method() == REFER && req.header(h_To).exists(p_tag))
31 {
32 // If this is an in-dialog REFER, then use a subscription id
33 mSubscriptionId = Data(req.header(h_CSeq).sequence());
34 }
35 Data key = getEventType() + getDocumentKey();
36 mDum.mServerSubscriptions.insert(DialogUsageManager::ServerSubscriptions::value_type(key, this));
37 }
38
39 ServerSubscription::~ServerSubscription()
40 {
41 DebugLog(<< "ServerSubscription::~ServerSubscription");
42
43 Data key = getEventType() + getDocumentKey();
44
45 std::pair<DialogUsageManager::ServerSubscriptions::iterator,DialogUsageManager::ServerSubscriptions::iterator> subs;
46 subs = mDum.mServerSubscriptions.equal_range(key);
47 for (DialogUsageManager::ServerSubscriptions::iterator i=subs.first; i!=subs.second; ++i)
48 {
49 if (i->second == this)
50 {
51 mDum.mServerSubscriptions.erase(i);
52 break;
53 }
54 }
55
56 mDialog.mServerSubscriptions.remove(this);
57 }
58
59 UInt32
60 ServerSubscription::getTimeLeft()
61 {
62 UInt32 timeleft = UInt32(mAbsoluteExpiry - Timer::getTimeSecs());
63 if (timeleft < 0) // .kw. this can NEVER happen since unsigned!
64 {
65 return 0;
66 }
67 else
68 {
69 return timeleft;
70 }
71 }
72
73 SharedPtr<SipMessage>
74 ServerSubscription::accept(int statusCode)
75 {
76 mDialog.makeResponse(*mLastResponse, mLastSubscribe, statusCode);
77 mLastResponse->header(h_Expires).value() = mExpires;
78 return mLastResponse;
79 }
80
81 SharedPtr<SipMessage>
82 ServerSubscription::reject(int statusCode)
83 {
84 if (statusCode < 300)
85 {
86 throw UsageUseException("Must reject with a code greater than or equal to 300", __FILE__, __LINE__);
87 }
88 mDialog.makeResponse(*mLastResponse, mLastSubscribe, statusCode);
89 return mLastResponse;
90 }
91
92
93 void
94 ServerSubscription::send(SharedPtr<SipMessage> msg)
95 {
96 ServerSubscriptionHandler* handler = mDum.getServerSubscriptionHandler(mEventType);
97 assert(handler);
98 if (msg->isResponse())
99 {
100 int code = msg->header(h_StatusLine).statusCode();
101 if (code < 200)
102 {
103 DialogUsage::send(msg);
104 }
105 else if (code < 300)
106 {
107 if(msg->exists(h_Expires))
108 {
109 mDum.addTimer(DumTimeout::Subscription, msg->header(h_Expires).value(), getBaseHandle(), ++mTimerSeq);
110 DialogUsage::send(msg);
111 mAbsoluteExpiry = Timer::getTimeSecs() + msg->header(h_Expires).value();
112 mSubDlgState = SubDlgEstablished;
113 }
114 else
115 {
116 throw UsageUseException("2xx to a Subscribe MUST contain an Expires header", __FILE__, __LINE__);
117 }
118 }
119 else if (code < 400)
120 {
121 DialogUsage::send(msg);
122 handler->onTerminated(getHandle());
123 delete this;
124 return;
125 }
126 else
127 {
128 if (shouldDestroyAfterSendingFailure(*msg))
129 {
130 DialogUsage::send(msg);
131 handler->onTerminated(getHandle());
132 delete this;
133 return;
134 }
135 else
136 {
137 DialogUsage::send(msg);
138 }
139 }
140 }
141 else
142 {
143 DialogUsage::send(msg);
144 if (mSubscriptionState == Terminated)
145 {
146 handler->onTerminated(getHandle());
147 delete this;
148 }
149 }
150 }
151
152 bool
153 ServerSubscription::shouldDestroyAfterSendingFailure(const SipMessage& msg)
154 {
155 int code = msg.header(h_StatusLine).statusCode();
156 switch(mSubDlgState)
157 {
158 case SubDlgInitial:
159 return true;
160 case SubDlgTerminating: //terminated state not using in ServerSubscription
161 assert(0);
162 return true;
163 case SubDlgEstablished:
164 {
165 if (code == 405)
166 {
167 return true;
168 }
169 switch (Helper::determineFailureMessageEffect(*mLastResponse))
170 {
171 case Helper::TransactionTermination:
172 case Helper::RetryAfter:
173 break;
174 case Helper::OptionalRetryAfter:
175 case Helper::ApplicationDependant:
176 // .bwc. Uh, no. ApplicationDependent should imply that the
177 // app-writer has decided what to do. We don't decide here. And
178 // OptionalRetryAfter certainly doesn't mean we should tear the
179 // Usage down.
180 // throw UsageUseException("Not a reasonable code to reject a SUBSCIRBE(refresh) inside a dialog.",
181 // __FILE__, __LINE__);
182 break;
183 case Helper::DialogTermination: //?dcm? -- throw or destroy this?
184 case Helper::UsageTermination:
185 return true;
186 }
187 break;
188 }
189 default: // !jf!
190 assert(0);
191 break;
192
193 }
194 return false;
195 }
196
197 void
198 ServerSubscription::setSubscriptionState(SubscriptionState state)
199 {
200 mSubscriptionState = state;
201 }
202
203 void
204 ServerSubscription::dispatch(const SipMessage& msg)
205 {
206 DebugLog( << "ServerSubscription::dispatch: " << msg.brief());
207
208 ServerSubscriptionHandler* handler = mDum.getServerSubscriptionHandler(mEventType);
209 assert(handler);
210
211 if (msg.isRequest())
212 {
213 //!dcm! -- need to have a mechanism to retrieve default & acceptable
214 //expiration times for an event package--part of handler API?
215 //added to handler for now.
216 mLastSubscribe = msg;
217
218 int errorResponseCode = 0;
219 handler->getExpires(msg,mExpires,errorResponseCode);
220 if (errorResponseCode >= 400)
221 {
222 handler->onError(getHandle(), msg);
223 SharedPtr<SipMessage> response = reject(errorResponseCode);
224
225 if (errorResponseCode == 423 && handler->hasMinExpires())
226 {
227 response->header(h_MinExpires).value() = handler->getMinExpires();
228 }
229 send(response);
230 return;
231 }
232
233 InviteSessionHandle invSession;
234 if (getAppDialog().isValid())
235 {
236 invSession = getAppDialog()->getInviteSession();
237 }
238
239 if (mExpires == 0)
240 {
241 /* This is to handle the case where mExpires is zero because the client
242 is attempting to poll. In order for polling to work, the subscription
243 handler needs to get the onNewSubscription call. .mjf.
244 */
245 if (mSubscriptionState == Invalid)
246 {
247 mSubscriptionState = Terminated;
248 if (mEventType != "refer" )
249 {
250 handler->onNewSubscription(getHandle(), msg);
251 }
252 else if (!invSession.isValid())
253 {
254 handler->onNewSubscriptionFromRefer(getHandle(), msg);
255 }
256 }
257
258 makeNotifyExpires();
259 handler->onExpiredByClient(getHandle(), msg, *mLastRequest);
260
261 mDialog.makeResponse(*mLastResponse, mLastSubscribe, 200);
262 mLastResponse->header(h_Expires).value() = mExpires;
263 send(mLastResponse);
264
265 send(mLastRequest); // Send Notify Expires
266 return;
267 }
268 if (mSubscriptionState == Invalid)
269 {
270 //!dcm! -- should initial state be pending?
271 mSubscriptionState = Init;
272 if (mEventType != "refer")
273 {
274 DebugLog(<< "onNewSubscription called");
275 handler->onNewSubscription(getHandle(), msg);
276 }
277 else if (!invSession.isValid())
278 {
279 DebugLog(<< "onNewSubscriptionFromRefer called");
280 handler->onNewSubscriptionFromRefer(getHandle(), msg);
281 }
282 }
283 else
284 {
285 DebugLog(<< "onRefresh called");
286 handler->onRefresh(getHandle(), msg);
287 }
288 }
289 else
290 {
291 //.dcm. - will need to change if retry-afters are reaching here
292 mLastRequest->releaseContents();
293 int code = msg.header(h_StatusLine).statusCode();
294
295 if(code < 200)
296 {
297 return;
298 }
299 else if (code < 300)
300 {
301 handler->onNotifyAccepted(getHandle(), msg);
302 return;
303 }
304 else if (code < 400)
305 {
306 //in dialog NOTIFY got redirected? Bizarre...
307 handler->onError(getHandle(), msg);
308 handler->onTerminated(getHandle());
309 delete this;
310 }
311 else
312 {
313 switch(Helper::determineFailureMessageEffect(msg))
314 {
315 case Helper::TransactionTermination:
316 DebugLog( << "ServerSubscription::TransactionTermination: " << msg.brief());
317 handler->onNotifyRejected(getHandle(), msg);
318 break;
319 case Helper::UsageTermination:
320 case Helper::RetryAfter:
321 case Helper::OptionalRetryAfter:
322 case Helper::ApplicationDependant:
323 case Helper::DialogTermination:
324 DebugLog( << "ServerSubscription::UsageTermination: " << msg.brief());
325 handler->onError(getHandle(), msg);
326 handler->onTerminated(getHandle());
327 delete this;
328 break;
329 }
330 }
331 }
332 }
333
334 void
335 ServerSubscription::makeNotifyExpires()
336 {
337 mSubscriptionState = Terminated;
338 makeNotify();
339 mLastRequest->header(h_SubscriptionState).param(p_reason) = getTerminateReasonString(Timeout);
340 }
341
342 void
343 ServerSubscription::makeNotify()
344 {
345 mDialog.makeRequest(*mLastRequest, NOTIFY);
346 mLastRequest->header(h_SubscriptionState).value() = getSubscriptionStateString(mSubscriptionState);
347 if (mSubscriptionState == Terminated)
348 {
349 mLastRequest->header(h_SubscriptionState).remove(p_expires);
350 }
351 else
352 {
353 mLastRequest->header(h_SubscriptionState).param(p_expires) = getTimeLeft();
354 }
355
356 mLastRequest->header(h_Event).value() = mEventType;
357 if (!mSubscriptionId.empty())
358 {
359 mLastRequest->header(h_Event).param(p_id) = mSubscriptionId;
360 }
361 }
362
363
364 void
365 ServerSubscription::end(TerminateReason reason, const Contents* document, int retryAfter)
366 {
367 mSubscriptionState = Terminated;
368 makeNotify();
369 mLastRequest->header(h_SubscriptionState).param(p_reason) = getTerminateReasonString(reason);
370 if (document)
371 {
372 mLastRequest->setContents(document);
373 }
374 if (retryAfter != 0)
375 {
376 mLastRequest->header(h_SubscriptionState).param(p_retryAfter) = retryAfter;
377 }
378 send(mLastRequest);
379 }
380
381 void
382 ServerSubscription::end()
383 {
384 end(Timeout);
385 }
386
387 void
388 ServerSubscription::dispatch(const DumTimeout& timeout)
389 {
390 assert(timeout.type() == DumTimeout::Subscription);
391 if (timeout.seq() == mTimerSeq)
392 {
393 ServerSubscriptionHandler* handler = mDum.getServerSubscriptionHandler(mEventType);
394 assert(handler);
395 makeNotifyExpires();
396 handler->onExpired(getHandle(), *mLastRequest);
397 send(mLastRequest);
398 }
399 }
400
401 SharedPtr<SipMessage>
402 ServerSubscription::update(const Contents* document)
403 {
404 makeNotify();
405 mLastRequest->setContents(document);
406 return mLastRequest;
407 }
408
409 SharedPtr<SipMessage>
410 ServerSubscription::neutralNotify()
411 {
412 makeNotify();
413 mLastRequest->releaseContents();
414 return mLastRequest;
415 }
416
417 void
418 ServerSubscription::dialogDestroyed(const SipMessage& msg)
419 {
420 ServerSubscriptionHandler* handler = mDum.getServerSubscriptionHandler(mEventType);
421 assert(handler);
422 handler->onError(getHandle(), msg);
423 handler->onTerminated(getHandle());
424 delete this;
425 }
426
427 void
428 ServerSubscription::onReadyToSend(SipMessage& msg)
429 {
430 ServerSubscriptionHandler* handler = mDum.getServerSubscriptionHandler(mEventType);
431 assert(handler);
432 handler->onReadyToSend(getHandle(), msg);
433 }
434
435 void
436 ServerSubscription::flowTerminated()
437 {
438 // notify handler
439 ServerSubscriptionHandler* handler = mDum.getServerSubscriptionHandler(mEventType);
440 assert(handler);
441 handler->onFlowTerminated(getHandle());
442 }
443
444 EncodeStream&
445 ServerSubscription::dump(EncodeStream& strm) const
446 {
447 strm << "ServerSubscription " << mSubscriber;
448 return strm;
449 }
450
451
452 /* ====================================================================
453 * The Vovida Software License, Version 1.0
454 *
455 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
456 *
457 * Redistribution and use in source and binary forms, with or without
458 * modification, are permitted provided that the following conditions
459 * are met:
460 *
461 * 1. Redistributions of source code must retain the above copyright
462 * notice, this list of conditions and the following disclaimer.
463 *
464 * 2. Redistributions in binary form must reproduce the above copyright
465 * notice, this list of conditions and the following disclaimer in
466 * the documentation and/or other materials provided with the
467 * distribution.
468 *
469 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
470 * and "Vovida Open Communication Application Library (VOCAL)" must
471 * not be used to endorse or promote products derived from this
472 * software without prior written permission. For written
473 * permission, please contact vocal@vovida.org.
474 *
475 * 4. Products derived from this software may not be called "VOCAL", nor
476 * may "VOCAL" appear in their name, without prior written
477 * permission of Vovida Networks, Inc.
478 *
479 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
480 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
481 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
482 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
483 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
484 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
485 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
486 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
487 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
488 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
489 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
490 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
491 * DAMAGE.
492 *
493 * ====================================================================
494 *
495 * This software consists of voluntary contributions made by Vovida
496 * Networks, Inc. and many individuals on behalf of Vovida Networks,
497 * Inc. For more information on Vovida Networks, Inc., please see
498 * <http://www.vovida.org/>.
499 *
500 */

Properties

Name Value
svn:eol-style native
svn:mime-type text/plain

webmaster AT resiprocate DOT org
ViewVC Help
Powered by ViewVC 1.1.27