######## FAST PICKUP ROLE ######## modparam("htable", "htable", "park=>size=16;autoexpire=600") modparam("htable", "htable", "fp=>size=8"); kazoo.fast_pickup_cookies = KZ_FAST_PICKUP_COOKIES descr "maintains a hash table for correlating call-ids with media servers" kazoo.fast_pickup_realtime = KZ_FAST_PICKUP_REALTIME descr "queries channels api for realtime status of call-id" route[FAST_PICKUP_START] { $sht(fp=>count) = 0; } route[FAST_PICKUP_LOAD] { sht_reset("fp"); #!ifndef KZ_USE_DISPATCHER_LIST xlog("L_INFO", "$ci|log|fast|initializing fastpick hash table from dispatcher\n"); if (sql_xquery("exec", "select destination from dispatcher", "ra") == 1) { while($xavp(ra) != $null) { $var(host) = $(xavp(ra=>destination){uri.host}); $var(port) = $(xavp(ra=>destination){uri.port}); $var(destination) = $xavp(ra=>destination); $var(i) = 0; if(!is_ip("$var(host)")) { if(dns_query("$var(host)", "xyz")) { $var(destination) = $_s(sip:$dns(xyz=>addr[$var(i)]):$var(port)); } } xlog("L_INFO", "$ci|log|fast|adding key $(var(destination){s.md5}) for $var(destination)\n"); $sht(fp=>$(var(destination){s.md5})) = $var(destination); pv_unset("$xavp(ra)"); } } #!else xlog("L_INFO", "$ci|log|fast|cannot initialize hash table from dispatcher. check KZ_USE_DISPATCHER_LIST option in local.cfg\n"); #!endif } route[FAST_PICKUP_ATTEMPT] { if (!is_method("INVITE")) { return; } $var(replaced_call_id) = "none"; if($hdr(Replaces)!= $null) { $var(replaced_call_id) = $(hdr(Replaces){s.select,0,;}); } if($var(replaced_call_id) =~ "kfp+") { if($shtinc(fp=>count) == 1) { route(FAST_PICKUP_LOAD); } remove_hf_re("^Replaces"); $var(PickupOptions) = $(var(replaced_call_id){re.subst,/^kfp\+(.{2})([^@]*)@(.*)/\1/}{s.decode.hexa}); $var(md5) = $(var(replaced_call_id){re.subst,/^kfp\+(.{2})([^@]*)@(.*)/\2/}); $var(replaced_call_id) = $(var(replaced_call_id){re.subst,/^kfp\+(.{2})([^@]*)@(.*)/\3/}); if( $sht(fp=>$var(md5)) != $null) { route(FAST_PICKUP_OPTION); $du = $sht(fp=>$var(md5)); append_hf("Replaces: $var(replaced_call_id)$var(Pickup)\r\n"); xlog("L_INFO", "$ci|log|fast|found shortcut for call-id $var(replaced_call_id) , redirecting ($(ru{uri.user})) to $du\n"); route(EXTERNAL_TO_INTERNAL_RELAY); exit(); } else { $var(replaced_call_id) = "none"; xlog("L_INFO", "$ci|log|fast|shortcut $var(md5) invalid in this server, using standard routing\n"); } } if(@cfg_get.kazoo.fast_pickup_realtime == 1) { if($var(replaced_call_id) != "none") { xlog("L_INFO", "$ci|log|request has replaces call-id $var(replaced_call_id)\n"); $var(amqp_payload_request) = '{"Event-Category" : "call_event" , "Event-Name" : "channel_status_req", "Call-ID" : "' + $var(replaced_call_id) + '", "Active-Only" : true }'; $var(amqp_routing_key) = "call.status_req." + $(var(replaced_call_id){kz.encode}); sl_send_reply("100", "locating your call"); xlog("L_INFO", "$ci|log|querying cluster for the location of call-id $var(replaced_call_id)\n"); if(kazoo_query("callevt", $var(amqp_routing_key), $var(amqp_payload_request))) { $du = $(kzR{kz.json,Switch-URL}); if($du != $null) { if($(kzR{kz.json,Other-Leg-Call-ID}) == "") { ## not bridged $var(rep) = $_s($hdr(Replaces);a-leg=true); } else { ## ensure early-only=true $var(rep) = $_s($(hdr(Replaces){re.subst,/;early-only//});early-only=true); } remove_hf_re("^Replaces"); append_hf("Replaces: $var(rep)\r\n"); xlog("L_INFO", "$ci|log|call-id $var(replaced_call_id) found redirecting call ($(ru{uri.user})) to $du\n"); route(EXTERNAL_TO_INTERNAL_RELAY); exit(); } else { xlog("L_WARN", "$ci|log|call-id $var(replaced_call_id) not found in cluster, proceeding with normal dispatch\n"); } } else { xlog("L_WARN", "$ci|log|error querying cluster for call-id $var(replaced_call_id), proceeding with normal dispatch\n"); } } } ##### CALL-PARK #### if($sht(park=>$(ru{uri.user})@$(ru{uri.domain})) != $null) { $du = $sht(park=>$(ru{uri.user})@$(ruri{uri.domain})); xlog("L_INFO", "$ci|log|redirecting park request to $du\n"); route(EXTERNAL_TO_INTERNAL_RELAY); exit(); } } route[FAST_PICKUP_OPTION] { $var(Pickup) = ""; switch($var(PickupOptions)) { case 1: $var(Pickup) = ";a-leg=true"; break; case 2: $var(Pickup) = ";early-only=true"; break; default: break; } } route[FAST_PICKUP_INIT] { $var(AppName) = $(kzE{kz.json,App-Name}); ## park redirects without replaces header if($var(AppName) == "park") { if($(kzE{kz.json,State}) == "terminated") { $sht(park=>$(kzE{kz.json,Presence-ID})) = $null; } else { $sht(park=>$(kzE{kz.json,Presence-ID})) = $(kzE{kz.json,Switch-URI}); } } ## fast pickup with cookies if(@cfg_get.fast_pickup_cookies == 1) { if($var(AppName) == "park") { $var(Pickup) = 1; #";a-leg=true"; } else { $var(Pickup) = 2; #";early-only=true"; } $var(Option) = $(var(Pickup){s.encode.hexa}); $var(Cookie) = $(kzE{kz.json,Switch-URI}{s.md5}); $var(call_id) = $(kzE{kz.json,Call-ID}); $var(JObj) = $(kzE{re.subst,/"Call-ID"\s*\:\s*"([^"]*)"/"Call-ID" : "kfp+$var(Option)$var(Cookie)@\1"/}); xlog("L_INFO", "$var(call_id)|fast|shortcut ($var(Pickup)) kfp+$var(Option)$var(Cookie)@$var(call_id)\n"); } } # vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab