rework of 100rel support

The 100rel logic moved in great part to AmSipDialog; this way, it can be
hooked directly to request/reply processing routines, the ones on top of
the transactional layer (::updateStatus(..)).
This allows to:
- better control when to push the messages into the AmSession's
onSipRequest/~Reply, fixing some issues with applications that received
replies, but without sending themselves the requests
- insert needed headers, no matter which request function is used
  (::invite() vs. ..sendRequest())
sayer/1.4-spce2.6
bpintea 16 years ago
parent 448a56bda5
commit ce90db7205

@ -129,6 +129,10 @@ class SIPRegistration : public AmSipDialogEventHandler,
void onInvite2xx(const AmSipReply&) {}
void onNoAck(unsigned int) {}
void onNoPrack(const AmSipRequest &, const AmSipReply &) {}
void onInvite1xxRel(const AmSipReply &){}
void onPrack2xx(const AmSipReply &){}
void onFailure(AmSipDialogEventHandler::FailureCause cause,
const AmSipRequest*, const AmSipReply*){}
/** is this registration registered? */
bool active;

@ -356,8 +356,7 @@ int AmB2BSession::sendEstablishedReInvite() {
}
DBG("sending re-INVITE with saved session description\n");
return dlg.reinvite(get_100rel_hdr(reliable_1xx),
established_content_type, established_body,
return dlg.reinvite("", established_content_type, established_body,
SIP_FLAGS_VERBATIM);
}

@ -92,7 +92,7 @@ unsigned int AmConfig::OptionsSessionLimit = 0;
unsigned int AmConfig::OptionsSessionLimitErrCode = 503;
string AmConfig::OptionsSessionLimitErrReason = "Server overload";
unsigned char AmConfig::rel100 = REL100_SUPPORTED;
AmSipDialog::provisional_100rel AmConfig::rel100 = REL100_SUPPORTED;
vector <string> AmConfig::CodecOrder;

@ -30,6 +30,7 @@
#include "AmSdp.h"
#include "AmDtmfDetector.h"
#include "AmSipDialog.h"
#include <string>
using std::string;
@ -137,7 +138,7 @@ struct AmConfig
static unsigned int OptionsSessionLimitErrCode;
static string OptionsSessionLimitErrReason;
static unsigned char rel100;
static AmSipDialog::provisional_100rel rel100;
/** Time of no RTP after which Session is regarded as dead, 0 for no Timeout */
static unsigned int DeadRtpTime;

@ -66,7 +66,6 @@ AmSession::AmSession()
m_dtmfDetector(this), m_dtmfEventQueue(&m_dtmfDetector),
m_dtmfDetectionEnabled(true),
accept_early_session(false),
reliable_1xx(AmConfig::rel100),
refresh_method(REFRESH_UPDATE_FB_REINV),
processing_status(SESSION_PROCESSING_EVENTS)
#ifdef WITH_ZRTP
@ -79,8 +78,6 @@ AmSession::AmSession()
{
use_local_audio[AM_AUDIO_IN] = false;
use_local_audio[AM_AUDIO_OUT] = false;
if (reliable_1xx)
dlg.rseq = 0; //???
}
AmSession::~AmSession()
@ -687,40 +684,6 @@ void AmSession::onSipRequest(const AmSipRequest& req)
if(req.method == SIP_METH_INVITE){
switch(reliable_1xx) {
case REL100_SUPPORTED: /* if support is on, enforce if asked by UAC */
if (key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED),
SIP_EXT_100REL) ||
key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE),
SIP_EXT_100REL)) {
reliable_1xx = REL100_REQUIRE;
DBG(SIP_EXT_100REL " now active.\n");
}
break;
case REL100_REQUIRE: /* if support is required, reject if UAC doesn't */
if (! (key_in_list(getHeader(req.hdrs,SIP_HDR_SUPPORTED),
SIP_EXT_100REL) ||
key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE),
SIP_EXT_100REL))) {
ERROR("'" SIP_EXT_100REL "' extension required, but not advertised"
" by peer.\n");
dlg.reply(req, 421, "Extension Required", "", "",
SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF);
if (dlg.getStatus() < AmSipDialog::Connected)
setStopped();
return;
}
break; // 100rel required
default:
ERROR("BUG: unexpected value `%d' for '" SIP_EXT_100REL "' switch.",
reliable_1xx);
#ifndef NDEBUG
abort();
#endif
case 0: /* support disabled */
break;
}
try {
onInvite(req);
}
@ -765,23 +728,9 @@ void AmSession::onSipRequest(const AmSipRequest& req)
} else {
dlg.reply(req, 415, "Unsupported Media Type");
}
} else if( req.method == SIP_METH_PRACK ) {
if (reliable_1xx != REL100_REQUIRE) {
WARN("unexpected PRACK received while " SIP_EXT_100REL " not active.\n");
return;
}
if ((1<<MAX_RSEQ_BITS)<=req.rseq && req.rseq<=(unsigned)abs(dlg.rseq)) {
// call interface function
onPrack(req, LOC_RSEQ_ORDER(req.rseq));
if (req.rseq == (unsigned)-dlg.rseq) {
dlg.rseq = -dlg.rseq; // confirmed
DBG("latest RSeq (%u) confirmed.\n", dlg.rseq);
}
} else {
WARN("no matching RAck value in PRACK (%s).\n", req.hdrs.c_str());
return;
}
} else if (req.method == SIP_METH_PRACK) {
// TODO: SDP
dlg.reply(req, 200, "OK");
}
}
@ -876,47 +825,6 @@ void AmSession::onSipReply(const AmSipReply& reply,
} break;
default: break;// continue waiting.
}
// FIXME: should this stay under negotiate_onreply???
if (100 < reply.code && reply.code < 200 &&
reply.method == SIP_METH_INVITE) {
switch (reliable_1xx) {
case REL100_SUPPORTED:
if (key_in_list(getHeader(reply.hdrs, SIP_HDR_REQUIRE),
SIP_EXT_100REL))
reliable_1xx = REL100_REQUIRE;
else
break;
case REL100_REQUIRE: {
if (! reply.rseq) {
ERROR("no RSeq value (or unsupported 0) in reliable 1xx.\n");
dlg.cancel();
setStopped();
break;
}
string cseq_val = int2str(reply.cseq) + " " + reply.method;
sendPrack(/*rcvd sdp_offr [TODO]*/"",int2str(reply.rseq),cseq_val);
DBG(SIP_EXT_100REL " now active.\n");
}
break;
case 0:
// 100rel support disabled
break;
default:
ERROR("BUG: unexpected value `%d' for " SIP_EXT_100REL " switch.",
reliable_1xx);
#ifndef NDEBUG
abort();
#endif
} // switch reliable 1xx
} else if (300<=reply.code && reliable_1xx &&
reply.method==SIP_METH_PRACK) {
// if PRACK fails, tear down session
dlg.cancel();
setStopped();
} // if 1xx && INVITE || failed && PRACK
} // switch dlg status
} // status < Connected
} //if negotiate_onreply
@ -939,18 +847,11 @@ void AmSession::onNoAck(unsigned int cseq)
void AmSession::onNoPrack(const AmSipRequest &req, const AmSipReply &rpl)
{
INFO("reply <%s> timed out.\n", rpl.print().c_str());
if (100 < rpl.code && rpl.code < 200 && reliable_1xx == REL100_REQUIRE &&
(unsigned)dlg.rseq == rpl.rseq && rpl.method == SIP_METH_INVITE) {
INFO("reliable %d reply timed out; rejecting request.\n", rpl.code);
dlg.reply(req, 504, "Server Time-out");
// TODO: handle forking case (when more PRACKs are sent, out of which some
// might time-out/fail).
if (dlg.getStatus() < AmSipDialog::Connected)
setStopped();
} else {
WARN("reply timed-out, but not reliable.\n"); // debugging
}
dlg.reply(req, 504, "Server Time-out");
// TODO: handle forking case (when more PRACKs are sent, out of which some
// might time-out/fail).
if (dlg.getStatus() < AmSipDialog::Connected)
setStopped();
}
void AmSession::onAudioEvent(AmAudioEvent* audio_ev)
@ -982,12 +883,6 @@ void AmSession::onBye(const AmSipRequest& req)
setStopped();
}
void AmSession::onPrack(const AmSipRequest& req, unsigned cnt)
{
DBG("handling #%u PRACK.\n", cnt);
dlg.reply(req, 200, "OK");
}
int AmSession::acceptAudio(const string& body,
const string& hdrs,
string* sdp_reply)
@ -1050,46 +945,6 @@ void AmSession::onSendReply(const AmSipRequest& req, unsigned int code,
const string& reason, const string& content_type,
const string& body, string& hdrs, int flags)
{
if (req.method == SIP_METH_INVITE) {
if (100 < code && code < 200) {
switch (reliable_1xx) {
case REL100_SUPPORTED:
hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
break;
case REL100_REQUIRE:
// add Require HF
hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
// add RSeq HF
#ifndef NDEBUG
if ((abs(dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) ==
((1 << MAX_RSEQ_BITS) - 1)) {
ERROR("CRITICAL: RSeq value too high: increase MAX_RSEQ_BITS "
"(now %d) and recompile.\n", MAX_RSEQ_BITS);
abort();
}
#endif
if (dlg.rseq < 0) { // RSeq not yet PRACKed
// refuse subsequent 1xx if first isn't yet PRACKed
if ((((unsigned)-dlg.rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == 0)
throw AmSession::Exception(491, "last reliable 1xx not yet "
"PRACKed");
dlg.rseq --;
} else if (! dlg.rseq) { // only init rseq if 1xx is used
unsigned rseq_1st = (get_random() + 1) << MAX_RSEQ_BITS;
rseq_1st &= 0x7fffffff;
dlg.rseq = -((signed)rseq_1st);
} else {
dlg.rseq = -(++dlg.rseq);
}
hdrs += SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(-dlg.rseq) + CRLF;
break;
}
} else if (code < 300 && reliable_1xx == REL100_REQUIRE) {
if (dlg.rseq < 0) // reliable 1xx is pending
throw AmSession::Exception(491, "last reliable 1xx not yet PRACKed");
}
}
CALL_EVENT_H(onSendReply,req,code,reason,content_type,body,hdrs,flags);
}
@ -1137,19 +992,18 @@ int AmSession::sendUpdate(const string &cont_type, const string &body,
return dlg.update(cont_type, body, hdrs);
}
void AmSession::sendPrack(const string &sdp_offer,
const string &rseq_val,
const string &cseq_val)
void AmSession::onInvite1xxRel(const AmSipReply &reply)
{
string hdrs = "RAck: " + rseq_val + " " + cseq_val + "\r\n";
// TODO: digest an answer based on the sdp_offer
// TODO: should't cseq&rseq be handled in dialog, entirely?!?!
if (dlg.prack(/*cont. type*/"", /*body*/"", hdrs) < 0)
// TODO: SDP
if (dlg.prack(reply, /*cont. type*/"", /*body*/"", /*headers*/"") < 0)
ERROR("failed to send PRACK request in session '%s'.\n",sid4dbg().c_str());
}
void AmSession::onPrack2xx(const AmSipReply &reply)
{
/* TODO: SDP */
}
string AmSession::sid4dbg()
{
string dbg;
@ -1165,10 +1019,9 @@ int AmSession::sendReinvite(bool updateSDP, const string& headers, int flags)
RTPStream()->setLocalIP(AmConfig::LocalIP);
string sdp_body;
sdp.genResponse(advertisedIP(), RTPStream()->getLocalPort(), sdp_body);
return dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), SIP_APPLICATION_SDP,
sdp_body, flags);
return dlg.reinvite(headers, SIP_APPLICATION_SDP, sdp_body, flags);
} else {
return dlg.reinvite(headers + get_100rel_hdr(reliable_1xx), "", "", flags);
return dlg.reinvite(headers, "", "", flags);
}
}
@ -1183,8 +1036,7 @@ int AmSession::sendInvite(const string& headers)
// Generate SDP.
string sdp_body;
sdp.genRequest(advertisedIP(), RTPStream()->getLocalPort(), sdp_body);
return dlg.invite(headers + get_100rel_hdr(reliable_1xx), SIP_APPLICATION_SDP,
sdp_body);
return dlg.invite(headers, SIP_APPLICATION_SDP, sdp_body);
}
void AmSession::setOnHold(bool hold)
@ -1197,6 +1049,27 @@ void AmSession::setOnHold(bool hold)
unlockAudio();
}
void AmSession::onFailure(AmSipDialogEventHandler::FailureCause cause,
const AmSipRequest *req, const AmSipReply *rpl)
{
switch (cause) {
case FAIL_REL100:
if (rpl) {
dlg.cancel();
if (dlg.getStatus() < AmSipDialog::Connected)
setStopped();
} else if (req) {
dlg.reply(*req, 421, "Extension Required", "", "",
SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF);
if (dlg.getStatus() < AmSipDialog::Connected)
setStopped();
}
break;
default:
break;
}
}
// Utility for basic NAT handling: allow the config file to specify the IP
// address to use in SDP bodies
string AmSession::advertisedIP()

@ -166,13 +166,6 @@ protected:
/** do accept early session? */
bool accept_early_session;
/** enable the reliability of provisional replies? */
unsigned char reliable_1xx;
#define REL100_DISABLED 0
#define REL100_SUPPORTED 1
#define REL100_REQUIRE 2
#define REL100_MAX REL100_REQUIRE
vector<AmSessionEventHandler*> ev_handlers;
public:
@ -373,11 +366,6 @@ public:
/** send an INVITE */
virtual int sendInvite(const string& headers = "");
/** send a PRACK request */
void sendPrack(const string &sdp_offer,
const string &rseq_val,
const string &cseq_val);
/** set the session on/off hold */
virtual void setOnHold(bool hold);
@ -469,14 +457,6 @@ public:
*/
virtual void onCancel() {}
/**
* onPrack is called when a PRACK request is received for the session.
* The sequencing correctness (RAck fits) is already checked.
* Should be overridden if SDP offer is expected with it.
* @param cnt order of which 1xx this PRACK is for
*/
virtual void onPrack(const AmSipRequest& req, unsigned cnt);
/**
* onSessionStart will be called after call setup.
*
@ -528,6 +508,15 @@ public:
/** 2xx reply has been received for an INVITE transaction */
virtual void onInvite2xx(const AmSipReply& reply);
virtual void onInvite1xxRel(const AmSipReply &);
/** Hook called when an answer for a locally sent PRACK is received */
virtual void onPrack2xx(const AmSipReply &);
virtual void onFailure(AmSipDialogEventHandler::FailureCause cause,
const AmSipRequest*, const AmSipReply*);
#if 0
/** missing 2xx-ACK */
@ -602,21 +591,6 @@ inline AmRtpAudio* AmSession::RTPStream() {
return _rtp_str.get();
}
static inline string get_100rel_hdr(unsigned char reliable_1xx)
{
switch(reliable_1xx) {
case REL100_SUPPORTED:
return SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
case REL100_REQUIRE:
return SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
default:
ERROR("BUG: unexpected reliability switch value of '%d'.\n",
reliable_1xx);
case 0:
break;
}
return "";
}
#endif

@ -43,7 +43,9 @@ const char* AmSipDialog::status2str[4] = {
AmSipDialog::AmSipDialog(AmSipDialogEventHandler* h)
: status(Disconnected),cseq(10),r_cseq_i(false),hdl(h),pending_invites(0),
outbound_proxy(AmConfig::OutboundProxy),
force_outbound_proxy(AmConfig::ForceOutboundProxy)
force_outbound_proxy(AmConfig::ForceOutboundProxy),
reliable_1xx(AmConfig::rel100),
rseq(0)
{
}
@ -129,11 +131,68 @@ void AmSipDialog::updateStatus(const AmSipRequest& req)
local_party = req.to;
route = req.route;
}
if(hdl)
int cont = rel100OnRequestIn(req);
if(cont && hdl)
hdl->onSipRequest(req);
}
int AmSipDialog::rel100OnRequestIn(const AmSipRequest& req)
{
/* activate the 100rel, if needed */
if (req.method == SIP_METH_INVITE) {
switch(reliable_1xx) {
case REL100_SUPPORTED: /* if support is on, enforce if asked by UAC */
if (key_in_list(getHeader(req.hdrs, SIP_HDR_SUPPORTED),
SIP_EXT_100REL) ||
key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE),
SIP_EXT_100REL)) {
reliable_1xx = REL100_REQUIRE;
DBG(SIP_EXT_100REL " now active.\n");
}
break;
case REL100_REQUIRE: /* if support is required, reject if UAC doesn't */
if (! (key_in_list(getHeader(req.hdrs,SIP_HDR_SUPPORTED),
SIP_EXT_100REL) ||
key_in_list(getHeader(req.hdrs, SIP_HDR_REQUIRE),
SIP_EXT_100REL))) {
ERROR("'" SIP_EXT_100REL "' extension required, but not advertised"
" by peer.\n");
if (hdl) hdl->onFailure(FAIL_REL100, &req, 0);
return 0; // has been replied
}
break; // 100rel required
default:
ERROR("BUG: unexpected value `%d' for '" SIP_EXT_100REL "' switch.",
reliable_1xx);
#ifndef NDEBUG
abort();
#endif
case 0: /* support disabled */
break;
} // switch reliable_1xx
} else if (req.method == SIP_METH_PRACK) {
if (reliable_1xx != REL100_REQUIRE) {
WARN("unexpected PRACK received while " SIP_EXT_100REL " not active.\n");
// let if float up
} else if ((1<<MAX_RSEQ_BITS)<=req.rseq &&
req.rseq<=(unsigned)abs(rseq)) {
// reply generation will be done by the onSipRequest()
if (req.rseq == (unsigned)-rseq) {
rseq = -rseq; // confirmed
DBG("latest RSeq (%u) confirmed.\n", rseq);
}
} else {
WARN("no matching RAck value in PRACK (%s).\n", req.hdrs.c_str());
// just drop it, either a retransmission or some bogus
return 0;
}
}
return 1;
}
/**
*
* update dialog status from UAC Request that we send (e.g. INVITE)
@ -267,7 +326,6 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
break;
case Pending:
// TODO [SBGW]: if negative and PRACK, tear down the call (???)
case Disconnected:
// only change status of dialog if reply
// to INVITE received
@ -284,6 +342,8 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
break;
}
int cont = rel100OnReplyIn(reply);
// TODO: remove the transaction only after the dedicated timer has hit
// this would help taking care of multiple 2xx replies.
if(reply.code >= 200){
@ -304,10 +364,62 @@ void AmSipDialog::updateStatus(const AmSipReply& reply)
}
}
if(hdl)
if(cont && hdl)
hdl->onSipReply(reply, old_dlg_status, trans_method);
}
int AmSipDialog::rel100OnReplyIn(const AmSipReply &reply)
{
if (status!=Pending && status!=Connected)
return 1;
if (100<reply.code && reply.code<200 && reply.method==SIP_METH_INVITE) {
switch (reliable_1xx) {
case REL100_SUPPORTED:
if (key_in_list(getHeader(reply.hdrs, SIP_HDR_REQUIRE),
SIP_EXT_100REL))
reliable_1xx = REL100_REQUIRE;
// no break!
else
break;
case REL100_REQUIRE:
if (! reply.rseq) {
ERROR("no RSeq value (or unsupported 0) in reliable 1xx.\n");
if (hdl) hdl->onFailure(FAIL_REL100, 0, &reply);
} else {
DBG(SIP_EXT_100REL " now active.\n");
if (hdl) hdl->onInvite1xxRel(reply);
}
break;
case 0:
// 100rel support disabled
break;
default:
ERROR("BUG: unexpected value `%d' for " SIP_EXT_100REL " switch.",
reliable_1xx);
#ifndef NDEBUG
abort();
#endif
} // switch reliable 1xx
} else if (reliable_1xx && reply.method==SIP_METH_PRACK) {
if (300 <= reply.code) {
// if PRACK fails, tear down session
if (hdl) hdl->onFailure(FAIL_REL100, 0, &reply);
} else if (200 <= reply.code) {
if (hdl) hdl->onPrack2xx(reply);
} else {
WARN("received '%d' for " SIP_METH_PRACK " method.\n", reply.code);
}
// absorbe the replys for the prack (they've been dispatched through
// onPrack2xx, if necessary)
return 0;
}
return 1;
}
void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
{
assert(to_ev);
@ -320,7 +432,7 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
case AmSipTimeoutEvent::noPRACK:
DBG("Timeout: missing PRACK\n");
if(hdl) hdl->onNoPrack(to_ev->req, to_ev->rpl);
rel100OnTimeout(to_ev->req, to_ev->rpl);
break;
case AmSipTimeoutEvent::_noEv:
@ -331,6 +443,20 @@ void AmSipDialog::uasTimeout(AmSipTimeoutEvent* to_ev)
to_ev->processed = true;
}
void AmSipDialog::rel100OnTimeout(const AmSipRequest &req,
const AmSipReply &rpl)
{
INFO("reply <%s> timed out (not PRACKed).\n", rpl.print().c_str());
if (100 < rpl.code && rpl.code < 200 && reliable_1xx == REL100_REQUIRE &&
(unsigned)rseq == rpl.rseq && rpl.method == SIP_METH_INVITE) {
INFO("reliable %d reply timed out; rejecting request.\n", rpl.code);
if(hdl) hdl->onNoPrack(req, rpl);
} else {
WARN("reply timed-out, but not reliable.\n"); // debugging
}
}
bool AmSipDialog::getUACTransPending() {
return !uac_trans.empty();
}
@ -382,6 +508,8 @@ int AmSipDialog::reply(const AmSipRequest& req,
hdl->onSendReply(req,code,reason,
content_type,body,m_hdrs,flags);
rel100OnReplyOut(req, code, m_hdrs);
AmSipReply reply;
reply.method = req.method;
@ -415,6 +543,53 @@ int AmSipDialog::reply(const AmSipRequest& req,
return ret;
}
void AmSipDialog::rel100OnReplyOut(const AmSipRequest &req, unsigned int code,
string &hdrs)
{
if (req.method == SIP_METH_INVITE) {
if (100 < code && code < 200) {
switch (reliable_1xx) {
case REL100_SUPPORTED:
hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
break;
case REL100_REQUIRE:
// add Require HF
hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
// add RSeq HF
#ifndef NDEBUG
if ((abs(rseq) & ((1 << MAX_RSEQ_BITS) - 1)) ==
((1 << MAX_RSEQ_BITS) - 1)) {
ERROR("CRITICAL: RSeq value too high: increase MAX_RSEQ_BITS "
"(now %d) and recompile.\n", MAX_RSEQ_BITS);
abort();
}
#endif
if (rseq < 0) { // RSeq not yet PRACKed
// refuse subsequent 1xx if first isn't yet PRACKed
if ((((unsigned)-rseq) & ((1 << MAX_RSEQ_BITS) - 1)) == 0)
throw AmSession::Exception(491, "last reliable 1xx not yet "
"PRACKed");
rseq --;
} else if (! rseq) { // only init rseq if 1xx is used
unsigned rseq_1st = (get_random() + 1) << MAX_RSEQ_BITS;
rseq_1st &= 0x7fffffff;
rseq = -((signed)rseq_1st);
} else {
rseq = -(++rseq);
}
hdrs += SIP_HDR_COLSP(SIP_HDR_RSEQ) + int2str(-rseq) + CRLF;
break;
default:
break;
}
} else if (code < 300 && reliable_1xx == REL100_REQUIRE) {
if (rseq < 0) // reliable 1xx is pending
throw AmSession::Exception(491, "last reliable 1xx not yet PRACKed");
}
}
}
/* static */
int AmSipDialog::reply_error(const AmSipRequest& req, unsigned int code,
const string& reason, const string& hdrs)
@ -587,15 +762,16 @@ int AmSipDialog::transfer(const string& target)
return 0;
}
int AmSipDialog::prack(const string &cont_type,
int AmSipDialog::prack(const AmSipReply &reply1xx,
const string &cont_type,
const string &body,
const string &hdrs)
{
switch(status) {
case Pending:
case Connected:
break;
case Disconnected:
case Connected:
case Disconnecting:
ERROR("can not send PRACK while dialog is in state '%d'.\n", status);
return -1;
@ -603,7 +779,12 @@ int AmSipDialog::prack(const string &cont_type,
ERROR("BUG: unexpected dialog state '%d'.\n", status);
return -1;
}
return sendRequest(SIP_METH_PRACK, cont_type, body, hdrs);
string h = hdrs +
SIP_HDR_COLSP(SIP_HDR_RACK) +
int2str(reply1xx.rseq) + " " +
int2str(reply1xx.cseq) + " " +
reply1xx.method + CRLF;
return sendRequest(SIP_METH_PRACK, cont_type, body, h);
}
int AmSipDialog::cancel()
@ -633,6 +814,8 @@ int AmSipDialog::sendRequest(const string& method,
if(hdl)
hdl->onSendRequest(method,content_type,body,m_hdrs,flags,cseq);
rel100OnRequestOut(method, m_hdrs);
AmSipRequest req;
req.method = method;
@ -692,6 +875,27 @@ int AmSipDialog::sendRequest(const string& method,
return 0;
}
void AmSipDialog::rel100OnRequestOut(const string &method, string &hdrs)
{
if (method!=SIP_METH_INVITE) // && method!=SIP_METH_OPTIONS)
return;
switch(reliable_1xx) {
case REL100_SUPPORTED:
hdrs += SIP_HDR_COLSP(SIP_HDR_SUPPORTED) SIP_EXT_100REL CRLF;
return;
case REL100_REQUIRE:
hdrs += SIP_HDR_COLSP(SIP_HDR_REQUIRE) SIP_EXT_100REL CRLF;
return;
default:
ERROR("BUG: unexpected reliability switch value of '%d'.\n",
reliable_1xx);
case 0:
break;
}
}
string AmSipDialog::get_uac_trans_method(unsigned int cseq)
{
TransMap::iterator t = uac_trans.find(cseq);

@ -103,6 +103,19 @@ class AmSipDialogEventHandler
/** Hook called when a UAS INVITE transaction did not receive the PRACK */
virtual void onNoPrack(const AmSipRequest &, const AmSipReply &)=0;
/** Hook called when a privisional reply is received with 100rel active */
virtual void onInvite1xxRel(const AmSipReply &)=0;
/** Hook called when an answer for a locally sent PRACK is received */
virtual void onPrack2xx(const AmSipReply &)=0;
enum FailureCause {
FAIL_REL100,
#define FAIL_REL100 AmSipDialogEventHandler::FAIL_REL100
};
virtual void onFailure(FailureCause cause, const AmSipRequest*,
const AmSipReply*)=0;
virtual ~AmSipDialogEventHandler() {};
};
@ -123,6 +136,14 @@ class AmSipDialog
int updateStatusReply(const AmSipRequest& req,
unsigned int code);
int rel100OnRequestIn(const AmSipRequest &req);
int rel100OnReplyIn(const AmSipReply &reply);
void rel100OnTimeout(const AmSipRequest &req, const AmSipReply &rpl);
void rel100OnRequestOut(const string &method, string &hdrs);
void rel100OnReplyOut(const AmSipRequest &req, unsigned int code,
string &hdrs);
public:
enum Status {
@ -156,6 +177,20 @@ class AmSipDialog
string next_hop_ip;
unsigned short next_hop_port;
/** enable the reliability of provisional replies? */
enum provisional_100rel { // could be a char
REL100_DISABLED,
#define REL100_DISABLED AmSipDialog::REL100_DISABLED
REL100_SUPPORTED,
#define REL100_SUPPORTED AmSipDialog::REL100_SUPPORTED
REL100_REQUIRE,
//REL100_PREFERED, //TODO
#define REL100_REQUIRE AmSipDialog::REL100_REQUIRE
REL100_MAX
#define REL100_MAX AmSipDialog::REL100_MAX
};
enum provisional_100rel reliable_1xx;
int rseq; // RSeq for next request (NOTE: keep it signed!)
unsigned int cseq; // Local CSeq for next request
bool r_cseq_i;
@ -212,7 +247,8 @@ class AmSipDialog
int cancel();
/** @return 0 on success */
int prack(const string &cont_type,
int prack(const AmSipReply &reply1xx,
const string &cont_type,
const string &body,
const string &hdrs);

Loading…
Cancel
Save