Sipper 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. data/sipper/README.rb +26 -0
  2. data/sipper/b2bua_controller.rb +163 -0
  3. data/sipper/b2bua_session_mixin.rb +24 -0
  4. data/sipper/base_controller.rb +425 -0
  5. data/sipper/bin/common.rb +42 -0
  6. data/sipper/bin/generate.rb +70 -0
  7. data/sipper/bin/project.rb +44 -0
  8. data/sipper/bin/run.rb +85 -0
  9. data/sipper/bin/run_smoke.rb +8 -0
  10. data/sipper/config/log4r.xml +71 -0
  11. data/sipper/controller_class_loader.rb +29 -0
  12. data/sipper/controller_selector.rb +119 -0
  13. data/sipper/controllers/invite_controller.rb +30 -0
  14. data/sipper/controllers/order.yaml +3 -0
  15. data/sipper/custom_message.rb +4 -0
  16. data/sipper/detached_session.rb +11 -0
  17. data/sipper/docs/manual.txt +1621 -0
  18. data/sipper/generators/README +12 -0
  19. data/sipper/generators/gen_controller.rb +228 -0
  20. data/sipper/generators/gen_project.rb +45 -0
  21. data/sipper/generators/gen_test.rb +72 -0
  22. data/sipper/generators/project_template_dir/Rakefile +56 -0
  23. data/sipper/generators/project_template_dir/config/sipper.cfg +31 -0
  24. data/sipper/generators/project_template_dir/controllers/README.txt +2 -0
  25. data/sipper/generators/project_template_dir/dot_sipper.proj +2 -0
  26. data/sipper/generators/project_template_dir/logs/README.txt +2 -0
  27. data/sipper/generators/project_template_dir/tests/README.txt +2 -0
  28. data/sipper/lib/smc/statemap.rb +194 -0
  29. data/sipper/logs/dialog_info_store +0 -0
  30. data/sipper/logs/r.cmd +6 -0
  31. data/sipper/logs/r.sh +6 -0
  32. data/sipper/media/sipper_media_client.rb +268 -0
  33. data/sipper/media/sipper_media_event.rb +43 -0
  34. data/sipper/media/sipper_media_manager.rb +145 -0
  35. data/sipper/media/sipper_media_proxy.rb +60 -0
  36. data/sipper/media/sipper_offer_answer.rb +285 -0
  37. data/sipper/message.rb +512 -0
  38. data/sipper/modified_pattern_formatter.rb +119 -0
  39. data/sipper/proxy_controller.rb +143 -0
  40. data/sipper/registration.rb +52 -0
  41. data/sipper/request.rb +109 -0
  42. data/sipper/response.rb +123 -0
  43. data/sipper/ruby_ext/module.rb +27 -0
  44. data/sipper/ruby_ext/mutable_class.rb +17 -0
  45. data/sipper/ruby_ext/object.rb +38 -0
  46. data/sipper/ruby_ext/pqueue.rb +190 -0
  47. data/sipper/ruby_ext/snapshot.rb +201 -0
  48. data/sipper/ruby_ext/string.rb +18 -0
  49. data/sipper/ruby_ext/time.rb +9 -0
  50. data/sipper/run/run_sipper1.rb +28 -0
  51. data/sipper/run/run_sipper2.rb +56 -0
  52. data/sipper/sdp/sdp.rb +257 -0
  53. data/sipper/sdp/sdp_generator.rb +131 -0
  54. data/sipper/sdp/sdp_parser.rb +136 -0
  55. data/sipper/session.rb +1952 -0
  56. data/sipper/session_manager.rb +170 -0
  57. data/sipper/session_recorder.rb +190 -0
  58. data/sipper/session_state/DialogState.sm +54 -0
  59. data/sipper/session_state/DialogState_sm.rb +337 -0
  60. data/sipper/session_state/dialog_routes.rb +141 -0
  61. data/sipper/sip_headers/header.rb +632 -0
  62. data/sipper/sip_headers/sipuri.rb +352 -0
  63. data/sipper/sip_logger.rb +65 -0
  64. data/sipper/sip_message_router.rb +231 -0
  65. data/sipper/sip_test_driver_controller.rb +10 -0
  66. data/sipper/sipper.rb +329 -0
  67. data/sipper/sipper_assertions.rb +21 -0
  68. data/sipper/sipper_configurator.rb +376 -0
  69. data/sipper/sipper_http/sipper_http_request_dispatcher.rb +71 -0
  70. data/sipper/sipper_http/sipper_http_response.rb +25 -0
  71. data/sipper/stray_message_manager.rb +40 -0
  72. data/sipper/test_completion_signaling_helper.rb +77 -0
  73. data/sipper/transaction/Ict.sm +59 -0
  74. data/sipper/transaction/Ict_sm.rb +430 -0
  75. data/sipper/transaction/Ist.sm +74 -0
  76. data/sipper/transaction/Ist_sm.rb +460 -0
  77. data/sipper/transaction/Nict.sm +51 -0
  78. data/sipper/transaction/Nict_sm.rb +325 -0
  79. data/sipper/transaction/Nist.sm +59 -0
  80. data/sipper/transaction/Nist_sm.rb +356 -0
  81. data/sipper/transaction/invite_client_transaction.rb +274 -0
  82. data/sipper/transaction/invite_server_transaction.rb +319 -0
  83. data/sipper/transaction/non_invite_client_transaction.rb +230 -0
  84. data/sipper/transaction/non_invite_server_transaction.rb +263 -0
  85. data/sipper/transaction/state_machine_wrapper.rb +58 -0
  86. data/sipper/transaction/transaction.rb +212 -0
  87. data/sipper/transport/base_transport.rb +84 -0
  88. data/sipper/transport/rel_unrel.rb +19 -0
  89. data/sipper/transport/transport_and_route_resolver.rb +67 -0
  90. data/sipper/transport/udp_transport.rb +156 -0
  91. data/sipper/transport_manager.rb +33 -0
  92. data/sipper/udp_session.rb +17 -0
  93. data/sipper/util/command_element.rb +62 -0
  94. data/sipper/util/compact_converter.rb +50 -0
  95. data/sipper/util/counter.rb +26 -0
  96. data/sipper/util/digest/digest_authorizer.rb +204 -0
  97. data/sipper/util/expectation_parser.rb +164 -0
  98. data/sipper/util/locator.rb +31 -0
  99. data/sipper/util/message_fill.rb +58 -0
  100. data/sipper/util/persistence/ps_sipper_map.rb +63 -0
  101. data/sipper/util/persistence/sipper_map.rb +41 -0
  102. data/sipper/util/sipper_util.rb +305 -0
  103. data/sipper/util/timer/sip_timer_helper.rb +26 -0
  104. data/sipper/util/timer/timer_manager.rb +80 -0
  105. data/sipper/util/timer/timer_task.rb +56 -0
  106. data/sipper/util/validations.rb +44 -0
  107. data/sipper/version.rb +10 -0
  108. data/sipper_test/_test_media_uas.rb +79 -0
  109. data/sipper_test/base_test_case.rb +31 -0
  110. data/sipper_test/c_134.txt +7 -0
  111. data/sipper_test/driven_sip_test_case.rb +96 -0
  112. data/sipper_test/gold.txt +10 -0
  113. data/sipper_test/gold_res.txt +8 -0
  114. data/sipper_test/gold_sub.txt +9 -0
  115. data/sipper_test/hello_sipper.au +0 -0
  116. data/sipper_test/in_sipper.au +0 -0
  117. data/sipper_test/nonrr_proxy.rb +17 -0
  118. data/sipper_test/order_tests.yaml +4 -0
  119. data/sipper_test/rake_res.txt +399 -0
  120. data/sipper_test/rr_proxy.rb +17 -0
  121. data/sipper_test/run_test.cmd +94 -0
  122. data/sipper_test/sip_test_case.rb +104 -0
  123. data/sipper_test/test2xx_retransmission.rb +80 -0
  124. data/sipper_test/test2xx_retransmission_with_limit.rb +81 -0
  125. data/sipper_test/test2xx_retransmission_with_nist.rb +91 -0
  126. data/sipper_test/test2xx_retransmission_with_txns.rb +94 -0
  127. data/sipper_test/test_address_header.rb +66 -0
  128. data/sipper_test/test_b2bua1.rb +130 -0
  129. data/sipper_test/test_b2bua2.rb +120 -0
  130. data/sipper_test/test_b2bua3.rb +160 -0
  131. data/sipper_test/test_b2bua4.rb +130 -0
  132. data/sipper_test/test_base_controller.rb +110 -0
  133. data/sipper_test/test_base_transport.rb +37 -0
  134. data/sipper_test/test_cancel.rb +21 -0
  135. data/sipper_test/test_cancel_after2xx.rb +81 -0
  136. data/sipper_test/test_cancel_retransmission.rb +105 -0
  137. data/sipper_test/test_cancel_with481.rb +83 -0
  138. data/sipper_test/test_cancel_with487.rb +70 -0
  139. data/sipper_test/test_cancel_with_ist_without_nist.rb +99 -0
  140. data/sipper_test/test_cancel_with_nist.rb +84 -0
  141. data/sipper_test/test_cancel_without487.rb +77 -0
  142. data/sipper_test/test_command_element.rb +38 -0
  143. data/sipper_test/test_compact_converter.rb +33 -0
  144. data/sipper_test/test_controller_class_loader.rb +37 -0
  145. data/sipper_test/test_controller_selector.rb +53 -0
  146. data/sipper_test/test_controller_using_compact_headers.rb +74 -0
  147. data/sipper_test/test_controller_using_header_order.rb +85 -0
  148. data/sipper_test/test_controller_using_ict.rb +64 -0
  149. data/sipper_test/test_controller_using_ict_with_non_success.rb +72 -0
  150. data/sipper_test/test_controller_using_ict_with_tcbh.rb +24 -0
  151. data/sipper_test/test_controller_using_ict_with_tcbh_no_action.rb +24 -0
  152. data/sipper_test/test_controller_using_ist.rb +70 -0
  153. data/sipper_test/test_controller_using_ist_with_tcbh.rb +24 -0
  154. data/sipper_test/test_controller_using_nict.rb +115 -0
  155. data/sipper_test/test_controller_using_nict_with_tcbh.rb +24 -0
  156. data/sipper_test/test_controller_using_nist.rb +63 -0
  157. data/sipper_test/test_controller_using_pre_existing_rs0.rb +73 -0
  158. data/sipper_test/test_controller_using_pre_existing_rs1.rb +75 -0
  159. data/sipper_test/test_controller_using_pre_existing_rs2.rb +71 -0
  160. data/sipper_test/test_controller_using_route_set.rb +86 -0
  161. data/sipper_test/test_controller_with_sdp.rb +80 -0
  162. data/sipper_test/test_controllers/cancel/order.yaml +2 -0
  163. data/sipper_test/test_controllers/cancel/uac_cancel_controller.rb +35 -0
  164. data/sipper_test/test_controllers/cancel/uas_cancel_controller.rb +25 -0
  165. data/sipper_test/test_controllers/class_loading/ordered/first_ordered_controller.rb +5 -0
  166. data/sipper_test/test_controllers/class_loading/ordered/order.yaml +3 -0
  167. data/sipper_test/test_controllers/class_loading/ordered/recond_ordered_controller.rb +6 -0
  168. data/sipper_test/test_controllers/class_loading/ordered/second_ordered_controller.rb +6 -0
  169. data/sipper_test/test_controllers/class_loading/unordered/first_unordered_controller.rb +7 -0
  170. data/sipper_test/test_controllers/class_loading/unordered/second_unordered_controller.rb +6 -0
  171. data/sipper_test/test_controllers/ctrl_trhandler/lib/transport_filters/my_transport_handler.rb +22 -0
  172. data/sipper_test/test_controllers/ctrl_trhandler/uac_tr_handler_controller.rb +27 -0
  173. data/sipper_test/test_controllers/ctrl_trhandler/uas_tr_handler_controller.rb +21 -0
  174. data/sipper_test/test_controllers/ete/order.yaml +1 -0
  175. data/sipper_test/test_controllers/ete/uac_controller.rb +39 -0
  176. data/sipper_test/test_controllers/ete/uas_controller.rb +34 -0
  177. data/sipper_test/test_controllers/extensions/extension_uac_controller.rb +24 -0
  178. data/sipper_test/test_controllers/extensions/extension_uas_controller.rb +35 -0
  179. data/sipper_test/test_controllers/extensions/lib/sipper_extensions/my_from_extension.rb +16 -0
  180. data/sipper_test/test_controllers/ict_tcbh/lib/transaction_handlers/app_ict_handler.rb +23 -0
  181. data/sipper_test/test_controllers/ict_tcbh/uac_ict_tcbh_controller.rb +27 -0
  182. data/sipper_test/test_controllers/ict_tcbh/uac_ict_tcbh_no_action_controller.rb +28 -0
  183. data/sipper_test/test_controllers/ict_tcbh/uas_ict_tcbh_controller.rb +29 -0
  184. data/sipper_test/test_controllers/ict_tcbh/uas_ict_tcbh_no_action_controller.rb +31 -0
  185. data/sipper_test/test_controllers/ist_tcbh/lib/app_ist_handler.rb +13 -0
  186. data/sipper_test/test_controllers/ist_tcbh/uac_ist_tcbh_controller.rb +22 -0
  187. data/sipper_test/test_controllers/ist_tcbh/uas_ist_tcbh_controller.rb +31 -0
  188. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/in_order.yaml +2 -0
  189. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/my_transport_handler1.rb +21 -0
  190. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/my_transport_handler2.rb +21 -0
  191. data/sipper_test/test_controllers/multi_trhandlers/lib/transport_filters/out_order.yaml +2 -0
  192. data/sipper_test/test_controllers/multi_trhandlers/uac_multi_tr_handler_controller.rb +27 -0
  193. data/sipper_test/test_controllers/multi_trhandlers/uas_multi_tr_handler_controller.rb +29 -0
  194. data/sipper_test/test_controllers/multiple/lib/blank_test.rb +2 -0
  195. data/sipper_test/test_controllers/multiple/uac_info_controller.rb +21 -0
  196. data/sipper_test/test_controllers/multiple/uac_msg_controller.rb +20 -0
  197. data/sipper_test/test_controllers/multiple/uas_info_controller.rb +15 -0
  198. data/sipper_test/test_controllers/multiple/uas_msg_controller.rb +14 -0
  199. data/sipper_test/test_controllers/nict_tcbh/lib/transaction_handlers/app_nict_handler.rb +13 -0
  200. data/sipper_test/test_controllers/nict_tcbh/uac_nict_tcbh_controller.rb +26 -0
  201. data/sipper_test/test_controllers/nict_tcbh/uas_nict_tcbh_controller.rb +20 -0
  202. data/sipper_test/test_controllers/state_machine_based/lib/CreditControl.sm +43 -0
  203. data/sipper_test/test_controllers/state_machine_based/lib/CreditControl_sm.rb +194 -0
  204. data/sipper_test/test_controllers/state_machine_based/order.yaml +1 -0
  205. data/sipper_test/test_controllers/state_machine_based/uac_message_controller.rb +34 -0
  206. data/sipper_test/test_controllers/state_machine_based/uas_message_controller.rb +44 -0
  207. data/sipper_test/test_controllers/stray_message/lib/sipper_extensions/my_stray_handler.rb +9 -0
  208. data/sipper_test/test_controllers/stray_message/stray_uac_controller.rb +24 -0
  209. data/sipper_test/test_controllers/stray_message/stray_uas_controller.rb +31 -0
  210. data/sipper_test/test_controllers/string/order.yaml +1 -0
  211. data/sipper_test/test_controllers/string/uac_controller.rb +29 -0
  212. data/sipper_test/test_controllers/string/uas_controller.rb +22 -0
  213. data/sipper_test/test_controllers/test_controller.rb +24 -0
  214. data/sipper_test/test_detached_session1.rb +78 -0
  215. data/sipper_test/test_dialog_routes.rb +139 -0
  216. data/sipper_test/test_digest_challenge1.rb +89 -0
  217. data/sipper_test/test_digest_challenge2.rb +90 -0
  218. data/sipper_test/test_dynamic_parse.rb +249 -0
  219. data/sipper_test/test_empty_sdp.rb +89 -0
  220. data/sipper_test/test_ete.rb +37 -0
  221. data/sipper_test/test_expectation_parser.rb +76 -0
  222. data/sipper_test/test_extensions.rb +28 -0
  223. data/sipper_test/test_freeze.rb +81 -0
  224. data/sipper_test/test_generated.rb +90 -0
  225. data/sipper_test/test_header_parameters.rb +75 -0
  226. data/sipper_test/test_header_parse.rb +141 -0
  227. data/sipper_test/test_http_client1.rb +84 -0
  228. data/sipper_test/test_http_client2.rb +90 -0
  229. data/sipper_test/test_ict_with_timeout.rb +62 -0
  230. data/sipper_test/test_in_dialog_request.rb +61 -0
  231. data/sipper_test/test_inline_controller.rb +67 -0
  232. data/sipper_test/test_invite_client_transaction.rb +272 -0
  233. data/sipper_test/test_invite_replace.rb +147 -0
  234. data/sipper_test/test_invite_retransmission.rb +90 -0
  235. data/sipper_test/test_invite_server_transaction.rb +208 -0
  236. data/sipper_test/test_lower_cseq.rb +79 -0
  237. data/sipper_test/test_media.rb +52 -0
  238. data/sipper_test/test_message.rb +392 -0
  239. data/sipper_test/test_method_specific_response_handling.rb +81 -0
  240. data/sipper_test/test_multi_homed1.rb +127 -0
  241. data/sipper_test/test_multi_homed2.rb +109 -0
  242. data/sipper_test/test_multi_homed_duplicate.rb +87 -0
  243. data/sipper_test/test_multi_homed_with_detached.rb +88 -0
  244. data/sipper_test/test_multiple.rb +49 -0
  245. data/sipper_test/test_multiple_contacts.rb +89 -0
  246. data/sipper_test/test_non2xx_retransmission_with_ist.rb +87 -0
  247. data/sipper_test/test_non_invite_client_transaction.rb +210 -0
  248. data/sipper_test/test_non_invite_retransmission.rb +91 -0
  249. data/sipper_test/test_non_invite_server_transaction.rb +202 -0
  250. data/sipper_test/test_offer_answer_prack.rb +103 -0
  251. data/sipper_test/test_pickup.rb +258 -0
  252. data/sipper_test/test_prack.rb +105 -0
  253. data/sipper_test/test_prack_store_and_dispatch.rb +101 -0
  254. data/sipper_test/test_prack_timer.rb +105 -0
  255. data/sipper_test/test_ps_sipper_map.rb +56 -0
  256. data/sipper_test/test_re_invite_uas_ongoing_ict.rb +81 -0
  257. data/sipper_test/test_re_invite_uas_ongoing_ist.rb +84 -0
  258. data/sipper_test/test_re_invite_with_ongoing_ict.rb +101 -0
  259. data/sipper_test/test_re_invite_with_ongoing_ist.rb +89 -0
  260. data/sipper_test/test_recorder_swap.rb +157 -0
  261. data/sipper_test/test_refer.rb +121 -0
  262. data/sipper_test/test_registeration_clearing.rb +97 -0
  263. data/sipper_test/test_registrar.rb +109 -0
  264. data/sipper_test/test_registration_controller.rb +73 -0
  265. data/sipper_test/test_registration_timeout.rb +90 -0
  266. data/sipper_test/test_remote_controller.rb +39 -0
  267. data/sipper_test/test_remote_target_update_on2xx.rb +140 -0
  268. data/sipper_test/test_request.rb +106 -0
  269. data/sipper_test/test_response.rb +40 -0
  270. data/sipper_test/test_response_without_to_tag.rb +92 -0
  271. data/sipper_test/test_rport.rb +88 -0
  272. data/sipper_test/test_sdp.rb +91 -0
  273. data/sipper_test/test_sdp_parser.rb +145 -0
  274. data/sipper_test/test_session.rb +276 -0
  275. data/sipper_test/test_session_callback_handler.rb +68 -0
  276. data/sipper_test/test_session_lifetime.rb +66 -0
  277. data/sipper_test/test_session_manager.rb +141 -0
  278. data/sipper_test/test_session_recorder.rb +145 -0
  279. data/sipper_test/test_session_state_user_defined.rb +84 -0
  280. data/sipper_test/test_session_states.rb +91 -0
  281. data/sipper_test/test_simple_dialog_state.rb +86 -0
  282. data/sipper_test/test_simple_re_invite.rb +86 -0
  283. data/sipper_test/test_sip_uri.rb +234 -0
  284. data/sipper_test/test_sipper_util.rb +45 -0
  285. data/sipper_test/test_smc_controller.rb +17 -0
  286. data/sipper_test/test_smoke.rb +80 -0
  287. data/sipper_test/test_stray.rb +28 -0
  288. data/sipper_test/test_stray_inline.rb +89 -0
  289. data/sipper_test/test_stray_res_acked.rb +103 -0
  290. data/sipper_test/test_stray_respond.rb +104 -0
  291. data/sipper_test/test_stray_retry.rb +92 -0
  292. data/sipper_test/test_stray_retry_failure.rb +93 -0
  293. data/sipper_test/test_stray_retry_initial.rb +100 -0
  294. data/sipper_test/test_strict_router_with_angled.rb +84 -0
  295. data/sipper_test/test_string_record.rb +31 -0
  296. data/sipper_test/test_subscribe_notify.rb +141 -0
  297. data/sipper_test/test_subscribe_notify_client_timeout.rb +158 -0
  298. data/sipper_test/test_subscribe_notify_dialog.rb +146 -0
  299. data/sipper_test/test_subscribe_notify_expires.rb +155 -0
  300. data/sipper_test/test_subscribe_notify_multiple_subscription.rb +157 -0
  301. data/sipper_test/test_subscribe_notify_server_timeout.rb +144 -0
  302. data/sipper_test/test_subsequent_unsent.rb +88 -0
  303. data/sipper_test/test_target_refresh_proxy_detached.rb +128 -0
  304. data/sipper_test/test_target_refresh_proxy_udp.rb +130 -0
  305. data/sipper_test/test_target_refresh_rr_proxy_udp.rb +133 -0
  306. data/sipper_test/test_target_refresh_with_new_port.rb +158 -0
  307. data/sipper_test/test_target_refresh_with_proxy1.rb +113 -0
  308. data/sipper_test/test_target_refresh_with_rr_update_proxy.rb +145 -0
  309. data/sipper_test/test_target_refresh_with_update_proxy.rb +144 -0
  310. data/sipper_test/test_target_refresh_with_update_proxy2.rb +139 -0
  311. data/sipper_test/test_timer_manager.rb +71 -0
  312. data/sipper_test/test_timer_task.rb +60 -0
  313. data/sipper_test/test_transport_handler.rb +19 -0
  314. data/sipper_test/test_transport_multi_handler.rb +18 -0
  315. data/sipper_test/test_udp_transport.rb +119 -0
  316. data/sipper_test/test_unparsed.rb +70 -0
  317. data/sipper_test/test_validations.rb +69 -0
  318. data/sipper_test/test_with_rr_proxy.rb +111 -0
  319. data/sipper_test/testmediacontroller.rb +71 -0
  320. data/sipper_test/tracing.rb +23 -0
  321. data/sipper_test/transaction_test_helper.rb +176 -0
  322. data/sipper_test/transport_filters.rb +26 -0
  323. data/sipper_test/ts_sipper.rb +91 -0
  324. data/sipper_test/ts_smoke.rb +3 -0
  325. data/sipper_test/tt.cmd +20 -0
  326. metadata +455 -0
@@ -0,0 +1,136 @@
1
+
2
+ =begin
3
+
4
+ RFC 4566
5
+
6
+ Session description
7
+ v= (protocol version)
8
+ o= (originator and session identifier)
9
+ s= (session name)
10
+ i=* (session information)
11
+ u=* (URI of description)
12
+ e=* (email address)
13
+ p=* (phone number)
14
+ c=* (connection information -- not required if included in
15
+ all media)
16
+ b=* (zero or more bandwidth information lines)
17
+ One or more time descriptions ("t=" and "r=" lines; see below)
18
+ z=* (time zone adjustments)
19
+ k=* (encryption key)
20
+ a=* (zero or more session attribute lines)
21
+ Zero or more media descriptions
22
+
23
+ Time description
24
+ t= (time the session is active)
25
+ r=* (zero or more repeat times)
26
+
27
+ Media description, if present
28
+ m= (media name and transport address)
29
+ i=* (media title)
30
+ c=* (connection information -- optional if included at
31
+ session level)
32
+ b=* (zero or more bandwidth information lines)
33
+ k=* (encryption key)
34
+ a=* (zero or more media attribute lines)
35
+
36
+
37
+
38
+ Example -
39
+ v=0
40
+ o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5
41
+ s=SDP Seminar
42
+ i=A Seminar on the session description protocol
43
+ u=http://www.example.com/seminars/sdp.pdf
44
+ e=j.doe@example.com (Jane Doe)
45
+ c=IN IP4 224.2.17.12/127
46
+ t=2873397496 2873404696
47
+ a=recvonly
48
+ m=audio 49170 RTP/AVP 0
49
+ m=video 51372 RTP/AVP 99
50
+ a=rtpmap:99 h263-1998/90000
51
+
52
+ =end
53
+
54
+ require 'sdp/sdp'
55
+
56
+ module SDP
57
+
58
+ class SdpParser
59
+
60
+
61
+ def self._copy_default_from_session(session, h)
62
+ if session[:c]
63
+ h[:c] = session[:c] unless h[:c]
64
+ end
65
+
66
+ sessionStatus = nil
67
+
68
+ if session[:a]
69
+ session[:a].split("||").each do |val|
70
+ sessionStatus = "inactive" if val == "inactive"
71
+ sessionStatus = "sendrecv" if val == "sendrecv"
72
+ sessionStatus = "recvonly" if val == "recvonly"
73
+ sessionStatus = "sendonly" if val == "sendonly"
74
+ end
75
+ end
76
+
77
+ mediaStatus = nil
78
+
79
+ if h[:a]
80
+ h[:a].split("||").each do |val|
81
+ mediaStatus = "inactive" if val == "inactive"
82
+ mediaStatus = "sendrecv" if val == "sendrecv"
83
+ mediaStatus = "recvonly" if val == "recvonly"
84
+ mediaStatus = "sendonly" if val == "sendonly"
85
+ end
86
+ end
87
+
88
+ if (sessionStatus != nil) && (mediaStatus == nil)
89
+ unless h[:a]
90
+ h[:a] = sessionStatus
91
+ else
92
+ h[:a] << "||" << sessionStatus
93
+ end
94
+ end
95
+ end
96
+
97
+ def self.parse(arr, addDefault = false)
98
+ parsing_session = true
99
+ h = {}
100
+ sdp = SDP::Sdp.new
101
+ arr.each do |line|
102
+ line.strip!
103
+ next unless line =~ /=/
104
+ n, v = line.split("=", 2)
105
+ if n == "m"
106
+ if parsing_session
107
+ sdp.session_lines = h
108
+ h = {}
109
+ parsing_session = false
110
+ else
111
+ if addDefault
112
+ _copy_default_from_session(sdp.session_lines, h)
113
+ end
114
+ sdp.add_media_lines(h)
115
+ h = {}
116
+ end
117
+ end
118
+ if h[n.to_sym].nil?
119
+ h[n.to_sym] = v
120
+ else
121
+ h[n.to_sym] << "||" << v
122
+ end
123
+ end # each
124
+ if parsing_session
125
+ sdp.session_lines = h
126
+ else
127
+ if addDefault
128
+ _copy_default_from_session(sdp.session_lines, h)
129
+ end
130
+ sdp.add_media_lines(h)
131
+ end
132
+ return sdp
133
+ end
134
+ end # class
135
+
136
+ end
@@ -0,0 +1,1952 @@
1
+ require 'transport/udp_transport'
2
+ require 'transport/transport_and_route_resolver'
3
+ require 'util/message_fill'
4
+ require 'sip_logger'
5
+ require 'util/sipper_util'
6
+ require 'util/locator'
7
+ require 'util/validations'
8
+ require 'util/digest/digest_authorizer'
9
+ require 'sipper_configurator'
10
+ require 'response'
11
+ require 'request'
12
+ require 'session_manager'
13
+ require 'monitor'
14
+ require 'session_recorder'
15
+ require 'transaction/transaction'
16
+ require 'transaction/invite_client_transaction'
17
+ require 'transaction/invite_server_transaction'
18
+ require 'transaction/non_invite_client_transaction'
19
+ require 'transaction/non_invite_server_transaction'
20
+ require 'test_completion_signaling_helper'
21
+ require 'ostruct'
22
+ require 'rubygems'
23
+ gem 'facets', '= 1.8.54'
24
+ require 'facets/more/synchash'
25
+ require 'ruby_ext/snapshot'
26
+ require 'session_state/dialog_routes'
27
+ require 'b2bua_session_mixin'
28
+ require 'media/sipper_media_event'
29
+ require 'media/sipper_media_client'
30
+ require 'media/sipper_offer_answer'
31
+ require 'sdp/sdp'
32
+ require 'sdp/sdp_parser'
33
+ require 'sdp/sdp_generator'
34
+ require 'custom_message'
35
+ require 'registration'
36
+
37
+ class Session
38
+ include SipLogger
39
+ include SipperUtil
40
+ include SIP::Validations
41
+ include B2buaSessionMixin
42
+
43
+ attr_accessor :transport, :local_tag, :remote_tag, :prack_seq, :local_cseq, :remote_cseq,
44
+ :call_id, :our_contact, :max_fwd, :local_uri,
45
+ :remote_uri, :rip, :rp, :tp_flags, :imessage, :irequest, :iresponse, :imedia_event,
46
+ :session_map, :session_key, :half_dialog_key, :controller, :use_transactions, :use_ict,
47
+ :use_nict, :use_ist, :use_nist, :session_timer, :session_limit, :tmr_hash,
48
+ :use_2xx_retrans, :use_1xx_retrans, :t2xx_retrans_timers, :t1xx_retrans_timers,
49
+ :dialog_routes, :force_update_session_map, :reliable_1xx_status, :ihttp_response,
50
+ :subscriptionMap, :name, :offer_answer, :registrations
51
+
52
+ class SubscriptionData
53
+ attr_accessor :key, :timer, :source, :event, :event_id, :state, :method
54
+ end
55
+
56
+ snap_fields :@dialog_routes_snap, :@remote_tag, :@remote_uri, :@state
57
+
58
+ # todo check if there ever would be a case of receiving messages from
59
+ # different transports for the same session.
60
+ # Yes it is possible. UDP to TCP or TLS or back to UDP etc.
61
+ def initialize(*tipportrsl)
62
+ @local_tag = nil
63
+ @remote_tag = nil
64
+ #todo fix the local_tag and local_cseq story for R/R
65
+ @local_cseq = 0
66
+ @prack_seq = 10
67
+ @local_cseq_before_send = nil # this is set once when a request is created and reset on send
68
+ @remote_cseq = 0
69
+ @call_id = nil
70
+ @our_contact = nil
71
+ @max_fwd = 70
72
+ @local_uri = nil #"From" of out requests and To of out responses
73
+ @remote_uri = nil #"To" of out requests and From for out responses
74
+ @transport = tipportrsl[0]
75
+ @rip = tipportrsl[1]
76
+ @rp = tipportrsl[2]
77
+ @tp_flags = 0
78
+ @attr_hash = {}
79
+ @session_recorder = nil
80
+ @session_record = nil
81
+ @last_sent_request = nil
82
+ @last_sent_invite = nil
83
+ @pending_cancels = {} # keyed on txn key and value is cancel message
84
+ @cancellable_txns = []
85
+ @session_queue = []
86
+ @sq_lock = ["free"] #inuse or free, array because we want to keep the same object as we are mixing in a monitor
87
+ @sq_lock.extend(MonitorMixin)
88
+ # set the config for transaction usage
89
+ set_transaction_usage SipperConfigurator[:SessionTxnUsage]
90
+ @tmr_hash = {} # a hash of hash of transaction timer values for each type of Txns
91
+ SIP::Transaction::Transactions.each do |tname|
92
+ @tmr_hash[tname] = {}
93
+ end
94
+ @session_timer = SipperConfigurator[:SessionTimer]
95
+ @session_limit = SipperConfigurator[:SessionLimit]
96
+ @session_life_so_far = 0
97
+ @transactions = SyncHash.new # the total transcations of this session
98
+ @transaction_handlers = {} # the name of transaction handler classes, keyed with types.
99
+ @use_2xx_retrans = SipperConfigurator[:T2xxUsage] # boolean
100
+ @use_1xx_retrans = SipperConfigurator[:T1xxUsage] # boolean
101
+ @reliable_1xx_status = false
102
+ @t2xx_retrans_timers = {:Start=>SIP::Transaction::T1,
103
+ :Cap=>SIP::Transaction::T2,
104
+ :Limit=>(64*SIP::Transaction::T1)}
105
+ @t2xx_retrans_timers = @t2xx_retrans_timers.merge SipperConfigurator[:T2xxTimers] if SipperConfigurator[:T2xxTimers] # 3 timer settings for 2xx retransmission
106
+
107
+ @t1xx_retrans_timers = {:Start=>SIP::Transaction::T1,
108
+ :Limit=>(64*SIP::Transaction::T1)}
109
+ @t1xx_retrans_timers = @t1xx_retrans_timers.merge SipperConfigurator[:T1xxTimers] if SipperConfigurator[:T1xxTimers] # 2 timer settings for 1xx retransmission
110
+
111
+ @dialog_routes = DialogRoutes.new(tipportrsl[3])
112
+ @session_limit = tipportrsl[4] if tipportrsl[4]
113
+ @pending_response_queue = Queue.new
114
+ @subscriptionMap = {}
115
+ @state = ["initial"]
116
+ @user_defined_state = false
117
+ _schedule_timer_for_session(:session_limit, @session_limit)
118
+ @offer_answer = Media::SipperOfferAnswer.new(self)
119
+ end
120
+
121
+ def get_state_array
122
+ @state
123
+ end
124
+
125
+ def last_state
126
+ @state[-1]
127
+ end
128
+
129
+ def set_state(state)
130
+ @state = [state]
131
+ @user_defined_state = true
132
+ end
133
+
134
+ def remote_target
135
+ @dialog_routes.get_ruri_and_routes[0]
136
+ end
137
+
138
+ def remote_target=(rt)
139
+ @dialog_routes.remote_target = rt
140
+ end
141
+
142
+ def [](k)
143
+ @attr_hash[k]
144
+ end
145
+
146
+ def []=(k,v)
147
+ @attr_hash[k] = v
148
+ end
149
+
150
+
151
+ def _fixed_local_tag(ftag_from_msg=nil)
152
+ unless @local_tag
153
+ if ftag_from_msg && ftag_from_msg != "_PH_LCTG_"
154
+ @local_tag = ftag_from_msg
155
+ return @local_tag
156
+ end
157
+ unless @remote_tag
158
+ @local_tag = "2"
159
+ else
160
+ @local_tag = @remote_tag+"1"
161
+ end
162
+ end
163
+ @local_tag
164
+ end
165
+
166
+ def send(msg)
167
+ # todo: as performance improvement set only when needed.
168
+ unless msg.class < Message
169
+ err_msg = "Message is not a SIP message #{msg}"
170
+ log_and_raise err_msg, ArgumentError
171
+ end
172
+
173
+ # A final check to see if any route headers were pushed by the controller on the initial
174
+ # request and here we
175
+ # adjust the request uri and routes one last time only for initial requests.
176
+ if msg.class == Request && msg.initial
177
+ rrt = @dialog_routes.get_ruri_and_routes_for_pushed_routes(msg)
178
+ msg.uri = rrt[0] if rrt[0]
179
+ msg = _add_route_headers_if_present(msg, rrt)
180
+ end
181
+
182
+ _check_transport_and_destination(msg)
183
+ unless @transport && @rip && @rp
184
+ err_msg = "Cannot send message, as transport is not properly set #{msg}"
185
+ log_and_raise err_msg, RuntimeError
186
+ end
187
+ case msg
188
+ when Request
189
+ send_request(msg)
190
+ when Response
191
+ send_response(msg)
192
+ end
193
+ end
194
+
195
+
196
+ def send_request(msg)
197
+ if SipperConfigurator[:ProtocolCompliance] == 'strict'
198
+ msg.from.tag = '_PH_LCTG_' unless msg.from.tag
199
+ end
200
+ @local_uri, @remote_uri = begin
201
+ log_and_raise "Cannot send CANCEL as no response received so far" unless _check_cancel_state(msg) if (msg.method == "CANCEL" && SipperConfigurator[:ProtocolCompliance]=='strict')
202
+ logd("This is a request and is initial "+msg.initial.to_s)
203
+ if msg.initial
204
+ _increment_local_cseq
205
+ SipperUtil::MessageFill.fill(msg, :lcnts=>@local_cseq.to_s, :lctg=>_fixed_local_tag(msg.from.tag).to_s,
206
+ :lcntr=>@remote_cseq.to_s, :gcnt=>SipperUtil::Counter.instance.next.to_s, :rnd=>SipperUtil.trand,
207
+ :lip=>@transport.ip, :lp=>@transport.port.to_s,
208
+ :rip=>@rip, :rp=>@rp.to_s, :trans=>@transport.tid)
209
+
210
+ self.remote_target = msg.uri
211
+ end
212
+ if ((SipperConfigurator[:ProtocolCompliance]=='strict') &&
213
+ (@local_cseq_before_send) &&
214
+ (@local_cseq_before_send+1 != @local_cseq))
215
+ @local_cseq -= 1 # one step back
216
+ log_and_raise "The CSeq has increased by more than one, you may have created but not sent a request"
217
+ end
218
+ @max_fwd = msg.max_forwards.to_s.to_i
219
+ @last_sent_request = msg
220
+ if (msg.method == "INVITE")
221
+ @last_sent_invite = msg
222
+ end
223
+ [msg.from.to_s, msg.to.to_s]
224
+ end
225
+ @local_tag = msg.from.tag if msg.from.tag
226
+ @remote_tag = msg.to.tag if msg.to.tag
227
+
228
+
229
+ # todo this is where we will look for 1300 size and flip over the transports and modify Vias
230
+ # we will have to re-write our Via, so do a pop_via and a push_via and continue to send message
231
+ # in the transport layer we will once again look for filling up the values. Also keep the
232
+ # popped via such that if sending of message fails then we revert back to popped via.
233
+ # Same thing needs to be done for Contact and also Record-Route if proxy
234
+ #
235
+ if msg.method == "INVITE"
236
+ if self.use_ict
237
+ # check for existing transactions before sending re-invite
238
+ # 3261 14.1
239
+ # If there is an ongoing INVITE client transaction, the TU MUST wait until the
240
+ # transaction reaches the completed or terminated state before initiating the new INVITE.
241
+ # And
242
+ # If there is an ongoing INVITE server transaction, the TU MUST wait until the
243
+ # transaction reaches the confirmed or terminated state before initiating the new INVITE.
244
+ if !msg.initial && SipperConfigurator[:ProtocolCompliance]=='strict'
245
+ @transactions.values.each do |txn|
246
+ if txn.transaction_name == :Ict
247
+ unless ["IctMap.Completed", "IctMap.Terminated"].include? txn.state
248
+ rollback_to_unsent_state
249
+ log_and_raise "Another ICT #{txn} in progress, cannot send re-INVITE"
250
+ end
251
+ elsif txn.transaction_name == :Ist
252
+ unless ["IstMap.Confirmed", "IstMap.Terminated", "IstMap.Finished"].include? txn.state
253
+ rollback_to_unsent_state
254
+ log_and_raise "Another IST #{txn} in progress, cannot send re-INVITE"
255
+ end
256
+ end
257
+ end
258
+ end
259
+ branch = msg.via.branch
260
+ klass = @transaction_handlers[:Ict] || @transaction_handlers[:Base]
261
+ txn_handler = klass.new if klass
262
+ ict = SIP::Transaction::InviteClientTransaction.new(self, branch, txn_handler, transport, @tp_flags)
263
+ @tmr_hash[:Ict].each_pair {|k,v| ict.send("#{k}=".to_sym, v) } #check if sym required
264
+ msg.transaction = ict
265
+ @transactions[branch] = ict
266
+ end
267
+ elsif msg.method == "ACK"
268
+ # nothing special because ict txn is already terminated
269
+ else
270
+ if self.use_nict
271
+ branch = msg.via.branch
272
+ klass = @transaction_handlers[:Nict] || @transaction_handlers[:Base]
273
+ txn_handler = klass.new if klass
274
+ nict = SIP::Transaction::NonInviteClientTransaction.new(self, branch, txn_handler, transport, @tp_flags)
275
+ @tmr_hash[:Nict].each_pair {|k,v| nict.send("#{k}=".to_sym, v) } #check if sym required
276
+ msg.transaction = nict
277
+ if msg.method == "CANCEL"
278
+ ctx = @transactions[branch]
279
+ logd("Sending CANCEL found a Ctx #{ctx} for branch #{branch}")
280
+ if ctx
281
+ ctx.cancel_ctxn = nict # ict or even nict
282
+ else
283
+ @transactions[branch] = nict # in the unlikely event of no ctx
284
+ end
285
+ else
286
+ @transactions[branch] = nict
287
+ end
288
+ end
289
+ end
290
+ @local_cseq_before_send = nil unless msg.method == "ACK" || msg.method == "CANCEL"
291
+ @offer_answer.handle_outgoing_request(msg) if @offer_answer
292
+ _send_common(msg)
293
+ end
294
+
295
+
296
+ # 13.3.1.4
297
+ # Once the response has been constructed, it is passed to the INVITE server transaction.
298
+ # Note, however, that the INVITE server transaction will be destroyed as soon as it
299
+ # receives this final response and passes it to the transport. Therefore, it is
300
+ # necessary to periodically pass the response directly to the transport until the ACK arrives.
301
+ # The 2xx response is passed to the transport with an interval that starts at T1 seconds
302
+ # and doubles for each retransmission until it reaches T2 seconds (T1 and T2 are defined
303
+ # in Section 17). Response retransmissions cease when an ACK request for the response
304
+ # is received. This is independent of whatever transport protocols are used to send
305
+ # the response .
306
+ # Since 2xx is retransmitted end-to-end, there may be hops between UAS and UAC that are UDP.
307
+ # To ensure reliable delivery across these hops , the response is retransmitted
308
+ # periodically even if the transport at the UAS is reliable.
309
+ # If the server retransmits the 2xx response for 64*T1 seconds without receiving an ACK,
310
+ # the dialog is confirmed, but the session SHOULD be terminated. This is accomplished
311
+ # with a BYE, as described in Section 15.
312
+
313
+ def send_response(msg, check_txn=true)
314
+ if (@reliable_1xx_status == true && msg.get_request_method == "INVITE")
315
+ logd("Adding response message to pending queue as Reliable provision is in progress.")
316
+ @pending_response_queue << msg
317
+ @pending_response_queue << check_txn
318
+ return
319
+ end
320
+
321
+ @offer_answer.handle_outgoing_response(msg) if @offer_answer
322
+
323
+ @local_uri, @remote_uri = begin
324
+ [msg.to.to_s, msg.from.to_s]
325
+ end
326
+
327
+ @local_tag = msg.to.tag if msg.to.tag
328
+ @remote_tag = msg.from.tag if msg.from.tag
329
+
330
+ if check_txn
331
+ txn = @transactions[msg.via.branch] if msg.via
332
+ if txn
333
+ if msg.get_request_method != "CANCEL"
334
+ logd("Found transaction for branch #{msg.via.branch} giving it the response")
335
+ msg.transaction = txn
336
+ end
337
+ end
338
+ end
339
+
340
+
341
+ _send_common(msg)
342
+
343
+ if ((SipperUtil::SUCC_RANGE.include?msg.code) && (msg.get_request_method=="INVITE") && @use_2xx_retrans)
344
+ @ok_to_retrans_2xx = true
345
+ @two_xx = OpenStruct.new
346
+ @two_xx.response = msg
347
+ @two_xx.tp_flags = @tp_flags
348
+ rd = get_response_destination(msg)
349
+ @two_xx.rip = rd[1]
350
+ @two_xx.rp = rd[2]
351
+ @current_t2xx = @t2xx_retrans_timers[:Start]
352
+ _schedule_timer_for_session(:t2xx_timer, @current_t2xx)
353
+ _schedule_timer_for_session(:t2xx_limit_timer, @t2xx_retrans_timers[:Limit])
354
+ logd("Started the 2xx retransmission timer for #{self}")
355
+ end
356
+ if ((SipperUtil::RPROV_RANGE.include?msg.code) && (msg.get_request_method=="INVITE") && msg.rseq)
357
+ @reliable_1xx_status = true
358
+ @ok_to_retrans_1xx = true
359
+ @one_xx = OpenStruct.new
360
+ @one_xx.response = msg
361
+ @one_xx.tp_flags = @tp_flags
362
+ rd = get_response_destination(msg)
363
+ @one_xx.rip = rd[1]
364
+ @one_xx.rp = rd[2]
365
+ @current_t1xx = @t1xx_retrans_timers[:Start]
366
+ @active_1xx_timer.cancel if @active_1xx_timer
367
+
368
+ if @use_1xx_retrans
369
+ @active_1xx_timer = _schedule_timer_for_session(:t1xx_timer, @current_t1xx)
370
+ end
371
+ _schedule_timer_for_session(:t1xx_limit_timer, @t1xx_retrans_timers[:Limit])
372
+ logd("Started the 1xx retransmission timer for #{self}")
373
+ @last_sent_reliable_response = msg
374
+ end
375
+
376
+ if (@reliable_1xx_status == true && msg.get_request_method == "PRACK")
377
+ unless @active_1xx_timer && !@active_1xx_timer.canceled?
378
+ @reliable_1xx_status = false
379
+
380
+ if(@pending_response_queue.length >= 2)
381
+ msgval = @pending_response_queue.pop
382
+ chkval = @pending_response_queue.pop
383
+ send_response(msgval, chkval)
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ def _send_common(msg)
390
+ @call_id = msg.call_id.to_s unless @call_id
391
+ @our_contact = msg.contact.to_s if msg.contact
392
+ logd("In send_common @local_uri=#{@local_uri.to_s}, @remote_uri=#{@remote_uri.to_s}, @call_id=#{@call_id.to_s}")
393
+ raise StandardError if transport.nil?
394
+ # Now set the content length
395
+ msg.content_length = msg.content_len.to_s unless msg.respond_to?(:content_length) && msg.content_length && msg.content_length.respond_to?(:frozen_str) && msg.content_length.frozen_str
396
+ #logd("Sending msg #{msg}")
397
+ SessionManager.add_session self, ((msg.class == Response) && (SipperUtil::SUCC_RANGE.include?msg.code))
398
+ logd("Session map is #{@session_map}")
399
+ if @header_order_arr
400
+ msg.header_order = @header_order_arr
401
+ end
402
+ if @compact_header_arr
403
+ msg.compact_headers = @compact_header_arr
404
+ end
405
+ # now add a custom header to trace back message to session
406
+
407
+ msg.p_sipper_session = (self.name || self.to_s) if SipperConfigurator[:ShowSessionIdInMessages]
408
+ if msg.transaction
409
+ msg.transaction.txn_send msg
410
+ m_s = msg.transaction.msg_sent
411
+ else
412
+ if msg.is_request?
413
+ rd = get_request_destination()
414
+ elsif msg.is_response?
415
+ rd = get_response_destination(msg)
416
+ end
417
+ m_s = transport.send(msg, @tp_flags, rd[1], rd[2])
418
+ logd("Now record the outgoing message from session")
419
+ _do_record_sip("out", msg, m_s)
420
+ end
421
+ msg
422
+ end
423
+
424
+ #-- callbacks from Ict into TU (TU callbacks)
425
+ def transaction_transport_err(txn)
426
+ # todo simulate a 503 response
427
+ end
428
+
429
+ #++
430
+
431
+ # The timeout happens for IST on TimerH(completed state) and TimerI(confirmed state) expiry
432
+ # For ICT it happens for TimerB expiry when 408 is to be simulated
433
+ #
434
+ # NICT does not send 408 as per RFC 4320
435
+ # 4.2. Action 2
436
+ # A transaction-stateful SIP element MUST NOT send a response with
437
+ # Status-Code of 408 to a non-INVITE request. As a consequence, an
438
+ # element that cannot respond before the transaction expires will not
439
+ # send a final response at all.
440
+ # But this is a notification from the transaction to the TU and onwards to
441
+ # the controller.
442
+ def transaction_timeout(txn)
443
+ if (txn.transaction_name == :Ict || txn.transaction_name == :Nict)
444
+ req = txn.message
445
+ r = Response.create(408, "Transaction Timeout")
446
+ r.local = true
447
+ logd("In timeout, response now is #{r} and calling copy with request #{req}")
448
+ r.copy_from(req, :call_id, :cseq, :via, :to, :from) unless req.nil?
449
+ # note this thread is actually worker thread and not timer thread as
450
+ # we had queued this timer on the transport queue.
451
+ logd("Now sending 408 response locally for consumption to #{self}")
452
+ self.on_message(r)
453
+ end
454
+
455
+ end
456
+
457
+ def transaction_cleanup(txn)
458
+ @transactions.delete txn.branch_id
459
+ end
460
+
461
+ def transaction_wrong_state(txn)
462
+ # todo simulate a 503 response
463
+ end
464
+
465
+ # The rip and rp are set by looking at the dialog state, remote target
466
+ # route headers etc. However, if it cannot be ascertained from the
467
+ # message (like name instead of IP/port in RURI or Route) then we default
468
+ # to the rp and rip provided at the time of session creation.
469
+ def get_request_destination
470
+ [@transport, @rip, @rp]
471
+ end
472
+
473
+
474
+ # From RFC 3581
475
+ # When a server attempts to send a response, it examines the topmost
476
+ # Via header field value of that response. If the "sent-protocol"
477
+ # component indicates an unreliable unicast transport protocol, such as
478
+ # UDP, and there is no "maddr" parameter, but there is both a
479
+ # "received" parameter and an "rport" parameter, the response MUST be
480
+ # sent to the IP address listed in the "received" parameter, and the
481
+ # port in the "rport" parameter. The response MUST be sent from the
482
+ # same address and port that the corresponding request was received on.
483
+ # This effectively adds a new processing step between bullets two and
484
+ # three in Section 18.2.2 of SIP 3261
485
+ def get_response_destination(res)
486
+ if res.via
487
+ rip = res.via.received
488
+ if res.via.has_param?(:rport) && res.via.transport.downcase == "udp"
489
+ rp = res.via.rport
490
+ else
491
+ if x=res.via.sent_by_port
492
+ rp = x
493
+ else
494
+ rp = "5060" # not @rp here
495
+ end
496
+ end
497
+ else
498
+ rip = @rip
499
+ rp = @rp
500
+ end
501
+ [@transport, rip, rp]
502
+ end
503
+
504
+ #-- TU callbacks
505
+ #++
506
+
507
+ # Create an initial request, initial request is one which does not have any existing dialog
508
+ # state in sipper. Specifically the new initial request will not have a to tag.
509
+ def create_initial_request(method, uri, *rest)
510
+ log_and_raise "Cannot send initial request as some signaling has happened" unless initial_state?
511
+ self.remote_target = uri
512
+ rrt = @dialog_routes.get_ruri_and_routes
513
+ r = Request.create_initial(method, rrt[0], *rest)
514
+ r = _add_route_headers_if_present(r, rrt)
515
+ return r
516
+ end
517
+
518
+ def create_prack(response = @iresponse)
519
+ logd("Creating a prack request ")
520
+ _increment_local_cseq
521
+ h = {
522
+ :call_id => @call_id,
523
+ :from => @local_uri,
524
+ :to => @remote_uri,
525
+ :cseq => sprintf("%s PRACK", @local_cseq),
526
+ :via => sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s",
527
+ @transport.tid, @transport.ip, @transport.port.to_s,
528
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s),
529
+ :contact => @our_contact,
530
+ :max_forwards => @max_fwd.to_s,
531
+ :rack => sprintf("%s %s", response.rseq, response.cseq)
532
+ }
533
+
534
+ rrt = @dialog_routes.get_ruri_and_routes
535
+ r = Request.create_subsequent("PRACK", rrt[0], h)
536
+ r = _add_route_headers_if_present(r, rrt)
537
+ return r
538
+ end
539
+
540
+ def create_and_send_prack(response = @iresponse)
541
+ req = create_prack(response)
542
+ send(req)
543
+ end
544
+
545
+ # Create a new subsequent request. A subsequent request is created either within a dialog or for
546
+ # cases when a request is to be sent after some request was originally sent as an example a
547
+ # CANCEL request. However for generating a CANCEL use helper methods for it like create_cancel or
548
+ # create_and_send_cancel_when_ready
549
+ def create_subsequent_request(method, increment_cseq=true)
550
+ logd("Creating a subsequent request for #{method} and increment_cseq flag is #{increment_cseq}")
551
+ log_and_raise "As call_id is not set, it is likely that no initial req was sent or recvd" unless @call_id
552
+ if increment_cseq
553
+ _increment_local_cseq
554
+ end
555
+ if (method == "ACK")
556
+ h = {
557
+ :call_id => @call_id,
558
+ :from => @local_uri,
559
+ :to => @remote_uri,
560
+ :cseq => sprintf("%s %s", SipperUtil.cseq_number(@last_sent_invite.cseq), "ACK"),
561
+ :via => sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s-%s",
562
+ @transport.tid, @transport.ip, @transport.port.to_s,
563
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s,
564
+ SipperUtil.trand),
565
+ :contact => @our_contact,
566
+ :max_forwards => @max_fwd.to_s
567
+ }
568
+ else
569
+ h = {
570
+ :call_id => @call_id,
571
+ :from => @local_uri,
572
+ :to => @remote_uri,
573
+ :cseq => sprintf("%s %s", @local_cseq, method.upcase),
574
+ :via => sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s-%s",
575
+ @transport.tid, @transport.ip, @transport.port.to_s,
576
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s,
577
+ SipperUtil.trand),
578
+ :contact => @our_contact,
579
+ :max_forwards => @max_fwd.to_s
580
+ }
581
+ end
582
+
583
+ rrt = @dialog_routes.get_ruri_and_routes
584
+ r = Request.create_subsequent(method, rrt[0], h)
585
+ r = _add_route_headers_if_present(r, rrt)
586
+ return r
587
+ end
588
+
589
+ # Challenge here is the header object of WWW-Authenticate or Proxy-Authenticate header.
590
+ def create_request_with_response_to_challenge(challenge, proxy_challenge, user, passwd, lsr = @last_sent_request)
591
+ new_req = lsr.dup
592
+ new_req.initial = false
593
+ _increment_local_cseq
594
+ new_req.cseq = sprintf("%s %s", @local_cseq, lsr.method.upcase)
595
+ new_req.via = sprintf("SIP/2.0/%s %s:%s;branch=z9hG4bK-%s-%s-%s",
596
+ @transport.tid, @transport.ip, @transport.port.to_s,
597
+ @local_cseq, @remote_cseq, SipperUtil::Counter.instance.next.to_s)
598
+ if proxy_challenge
599
+ new_req.proxy_authorization = (@da.nil? ? @da = SipperUtil::DigestAuthorizer.new : @da).create_authorization_header(challenge, proxy_challenge, user, passwd, lsr)
600
+ else
601
+ new_req.authorization = (@da.nil? ? @da = SipperUtil::DigestAuthorizer.new : @da).create_authorization_header(challenge, proxy_challenge, user, passwd, lsr)
602
+ end
603
+
604
+ new_req
605
+ end
606
+
607
+ def create_response(code, phrase="SELECT", req=irequest, reliability=false)
608
+ log_and_raise "There is no request, cannot create response" unless req
609
+ code = Integer(code) unless code.is_a? Numeric
610
+ if @local_uri
611
+ if code > 100
612
+ @local_uri << ";tag=" << _fixed_local_tag.to_s unless @local_uri =~ /;tag=/
613
+ end
614
+ end
615
+ logd("@local_uri is now #{@local_uri.to_s}")
616
+ r = Response.create(code, phrase)
617
+ logd("Response now is #{r} and calling copy with request #{req}")
618
+ r.copy_from(req, :call_id, :cseq, :via, :to, :from, :record_route) unless req.nil?
619
+ r.to = @local_uri if @local_uri
620
+ r.contact = (@our_contact ||= sprintf("<sip:%s:%s;transport=%s>",
621
+ @transport.ip, @transport.port.to_s, @transport.tid)) unless code == 100
622
+
623
+ if ((req.method == "SUBSCRIBE") && (code >= 200) && (code < 300))
624
+ r.copy_from(req, :expires) unless req.nil?
625
+ end
626
+
627
+ if (reliability && code > 100 && code < 200)
628
+ r.require = "100rel"
629
+ r.rseq = sprintf("%d", @prack_seq)
630
+ @prack_seq = @prack_seq + 1
631
+ end
632
+
633
+ if req.method == "REGISTER" && @registrations
634
+ r.contact = nil
635
+ @registrations.each do |data|
636
+ r.add_contact(data.contact_uri)
637
+ r.contacts[-1].expires = data.expires
638
+ end
639
+ r.format_as_separate_headers_for_mv(:contact)
640
+ end
641
+ #logd("Response now is #{r}")
642
+ return r
643
+ end
644
+
645
+ # A simple operation to create and send the response to the request received
646
+ # todo automatically look up reason phrases
647
+ def respond_with(code, req=irequest, phrase=nil)
648
+ send(create_response(code, phrase, req))
649
+ end
650
+
651
+
652
+ def respond_reliably_with(code, req=irequest, phrase=nil)
653
+ raise ArgumentError, "Can only be used for provisional responses" if code < 101 || code > 200
654
+ send_response(create_response(code, phrase, req, true))
655
+ end
656
+
657
+ # A simple operation to create and send a request, initial or subsequent as the case
658
+ # may be.
659
+ def request_with(*args)
660
+ method = args[0].downcase
661
+ if(method == "cancel")
662
+ return create_and_send_cancel_when_ready
663
+ end
664
+
665
+ if(method == "ack")
666
+ return create_and_send_ack
667
+ end
668
+
669
+ if (initial_state?)
670
+ send(create_initial_request(*args))
671
+ else
672
+ send(create_subsequent_request(*args))
673
+ end
674
+ end
675
+
676
+
677
+ def initial_state?
678
+ @local_cseq+@remote_cseq == 0
679
+ end
680
+
681
+ # todo check for strict / lax operation waiting for prov. creating only for the INVITE
682
+ # request etc.
683
+ def create_cancel(lsr = @last_sent_invite)
684
+ log_and_raise "Cannot create CANCEL as no request was sent so far" unless lsr
685
+ rrt = @dialog_routes.get_ruri_and_routes
686
+ r = Request.create_subsequent("CANCEL", rrt[0], :cseq => sprintf("%s %s", SipperUtil.cseq_number(lsr.cseq), "CANCEL") )
687
+ r.copy_from(lsr, :call_id, :from, :to, :via, :contact, :max_forwards, :route)
688
+ r = _add_route_headers_if_present(r, rrt)
689
+ end
690
+
691
+ def create_and_send_cancel_when_ready(lsr = @last_sent_invite)
692
+ lsr = @last_sent_request unless lsr
693
+ c = create_cancel(lsr)
694
+ if _check_cancel_state(c)
695
+ send(c)
696
+ else
697
+ @pending_cancels[lsr.txn_id] = c
698
+ end
699
+ return c
700
+ end
701
+
702
+ # To be called by controller to generate an ACK for 2xx response.
703
+ #13.2.2.4
704
+ #The UAC core MUST generate an ACK request for each 2xx received from the transaction layer.
705
+ #The header fields of the ACK are constructed in the same way as for any request sent within
706
+ #a dialog (see Section 12) with the exception of the CSeq and the header fields related to authentication.
707
+ #The sequence number of the CSeq header field MUST be the same as the INVITE being acknowledged, but the CSeq
708
+ #method MUST be ACK. The ACK MUST contain the same credentials as the INVITE.
709
+ #If the 2xx contains an offer (based on the rules above), the ACK MUST
710
+ #carry an answer in its body. If the offer in the 2xx response is not acceptable, the
711
+ #UAC core MUST generate a valid answer in the ACK and then send a BYE immediately .
712
+ # Once the ACK has been constructed, the procedures of [4] are used to determine the destination address,
713
+ # port and transport. However, the request is passed to the transport layer directly for transmission,
714
+ # rather than a client transaction. This is because the UAC core handles retransmissions of the ACK,
715
+ # not the transaction layer. The ACK MUST be passed to the client transport every time a
716
+ # retransmission of the 2xx final response that triggered the ACK arrives.
717
+
718
+ def create_2xx_ack
719
+ #todo ACK must have the same credentials as INVITE.
720
+ ack = create_subsequent_request("ACK", false)
721
+ end
722
+
723
+
724
+ # Provided such that it can be used by controllers those are not using the transactions, otherwise this
725
+ # method is invoked from the ICT.
726
+ # 17.1.1.3
727
+ # The ACK request constructed by the client transaction MUST contain values for the Call-ID, From,
728
+ # and Request-URI that are equal to the values of those header fields in the request passed to the
729
+ # transport by the client transaction (call this the "original request" ). The To header field in the
730
+ # ACK MUST equal the To header field in the response being acknowledged, and therefore will usually
731
+ # differ from the To header field in the original request by the addition of the tag parameter.
732
+ # The ACK MUST contain a single Via header field, and this MUST be equal to the top Via header field
733
+ # of the original request. The CSeq header field in the ACK MUST contain the same value for the sequence
734
+ # number as was present in the original request, but the method parameter MUST be equal to "ACK".
735
+ # If the INVITE request whose response is being acknowledged had Route header fields, those header
736
+ # fields MUST appear in the ACK. This is to ensure that the ACK can be routed properly through
737
+ # any downstream stateless proxies.
738
+
739
+ def create_non_2xx_ack(invite=@last_sent_invite, response=@iresponse)
740
+ h = {
741
+ :to => response.to.to_s,
742
+ :cseq => sprintf("%s %s", SipperUtil.cseq_number(invite.cseq),"ACK")
743
+ }
744
+ ack = Request.create_subsequent("ACK", invite.uri, h)
745
+ ack.copy_from(invite, :call_id, :from, :via, :route, :contact, :max_forwards)
746
+ ack
747
+ end
748
+
749
+ # To be used only by the controllers on receipt of a response which will be used to figure out if
750
+ # the ACK is for 2xx or non-2xx. If it is required to generate an ACK to a response received previously
751
+ # (not the latest response)
752
+ # then create the ACK using the appropriate method (create_2xx_ack or create_non_2xx_ack) and then send
753
+ # it using session.send
754
+ #
755
+ def create_and_send_ack
756
+ send(create_ack)
757
+ end
758
+
759
+
760
+ def create_ack
761
+ if SipperUtil::SUCC_RANGE.include?@iresponse.code
762
+ a = create_2xx_ack
763
+ else
764
+ if use_ict
765
+ if SipperConfigurator[:ProtocolCompliance]=='strict'
766
+ log_and_raise "As InviteClientTransaction is in use, you MUST not send non-2xx ACK from here"
767
+ else
768
+ logw("As InviteClientTransaction is in use, you should not send non-2xx ACK from here")
769
+ end
770
+ end
771
+ a = create_non_2xx_ack
772
+ end
773
+ return a
774
+ end
775
+
776
+
777
+ # The media attributes set here shall be used in the next
778
+ # request or response sent from the controller.
779
+ # The argument is an attribute hash and can take the form
780
+ # set_media_attributes(:codec=>["G711U", "DTMF"] :type=>"SENDONLY", :play=>{:file=><file_name>})
781
+ # set_media_attributes can be called any number of times, as soon as
782
+ # ":codec" can be from of G711U, G711A or DTMF and will be in the form of an array and
783
+ # there may be more that one value for it. This can be added or removed at a later
784
+ # time with a new set_media_attributes() invocation. If a new codec is added
785
+ # then the earlier codec should still be listed in the array. Absense of codec
786
+ # from the list would mean that it is being withdrawn from the offers.
787
+ # ":type" can be one of SENDONLY or RECVONLY or SENDRECV
788
+ # ":play" can be hash of {file=> <file_to_play>, repeat=> true|false, duration=> <seconds>}
789
+ # ":play_spec" can be string "PLAY [file] [duration] | PLAY_REPEAT [file] [duration] | SLEEP [duration] | file"
790
+ # ":play_spec" can also be used to send out DTMF like "5,SLEEP 3,6,SLEEP 2,9"
791
+ # note if play_spec is present the play attribute will be ignored
792
+ # ":record_file" is the name of the file to record the incoming stream
793
+ # ":remote_m_line" the selected "m" line from the remote media stream. It can also be selected as
794
+ # the value "any" in which case the system selects the best option from the offer.
795
+ # ":remote_a_line" "a" line corresponding to the m line. This is an array, corresponding to
796
+ # the payloads.
797
+ # "remote_session_a_line" the "a" line (if any) from the session part of SDP.
798
+ # ":remote_c_line" the selected "c" line from session or media for the remote media stream
799
+ # This function can be called repeatedly, when you send out your offer and when to received
800
+ # an answer.
801
+ # As soon as media object has enough information the media is established. As an example you may have
802
+ # set attributes to set your prefered codec, type and what you want to play but do not have
803
+ # the remote information as you may have just sent out the offer. So initially you will set
804
+ # attributes with :codec, :type, :play. Then when you receive the 200 OK you may select a media line
805
+ # of your choice and call set_attributes with :remote_m_line and :remote_c_line.
806
+ # Alternatively you could have selected :remote_m_line as "any" upfront and on getting answer sipper
807
+ # could have selected the best answer from the given options.
808
+ def set_media_attributes(mattr)
809
+ play_spec = mattr[:play_spec]
810
+ dtmf_spec = mattr[:dtmf_spec]
811
+ rec_spec = mattr[:rec_spec]
812
+
813
+ play = mattr[:play]
814
+ unless play_spec
815
+ if play
816
+ play_spec = play[:repeat] ? "PLAY_REPEAT " : "PLAY "
817
+ play_spec << play[:file] if play[:file]
818
+ play_spec << " " << play[:duration] if play[:duration]
819
+ end
820
+ end
821
+ @offer_answer.setup_media_spec(play_spec, rec_spec, dtmf_spec) if @offer_answer
822
+ end
823
+
824
+ def update_dtmf_spec(mattr)
825
+ set_media_attributes(mattr)
826
+ end
827
+
828
+ def update_audio_spec(mattr)
829
+ set_media_attributes(mattr)
830
+ @offer_answer.refresh_sipper_media if @offer_answer
831
+ end
832
+
833
+
834
+ # todo write tests for on_message, on_request, on_response when doing IT
835
+ def on_message(r)
836
+ logd("session.on_message called for session #{self} and message #{r.short_to_s}")
837
+ @sq_lock.synchronize do
838
+ @session_queue << r
839
+ if @sq_lock[0] == "free"
840
+ @sq_lock[0] = "inuse"
841
+ else
842
+ return
843
+ end
844
+ end
845
+ msg = nil
846
+ loop do
847
+ logd("Looking for a new message from session level queue")
848
+ @sq_lock.synchronize do
849
+ msg = @session_queue.shift
850
+ unless msg
851
+ @sq_lock[0] = "free"
852
+ return
853
+ end
854
+ end
855
+ logd("In session.on_message now processing message #{msg}")
856
+ case msg
857
+ when Request
858
+ _on_request(msg)
859
+ when Response
860
+ _on_response(msg)
861
+ when SIP::TimerTask
862
+ _on_timer(msg) unless msg.canceled?
863
+ when Media::SipperMediaEvent
864
+ _on_media_event(msg)
865
+ when SipperHttp::SipperHttpResponse
866
+ _on_http_response(msg)
867
+ when CustomMessage
868
+ _on_custom_message(msg)
869
+ end
870
+ end
871
+ end
872
+
873
+ # invoked on incoming request
874
+ def _on_request(request)
875
+ _update_and_check_dialog_state_on_request(request)
876
+ @offer_answer.handle_incoming_request(request) if @offer_answer
877
+
878
+ # We forward the CANCEL to the transaction being canceled only after the
879
+ # NIST processing of the CANCEL is over. So typically a 200/CANCEL shall precede
880
+ # the 487/INVITE.
881
+ forward_cancel_to_stxn = false
882
+ nist = nil
883
+ stxn = nil
884
+
885
+ if request.method == "PRACK"
886
+ if request[:rack] && @last_sent_reliable_response && @last_sent_reliable_response[:rseq]
887
+ if request.rack.header_value.strip.split(' ')[0] == @last_sent_reliable_response.rseq.header_value.strip
888
+ @ok_to_retrans_1xx = false
889
+ @active_1xx_timer.cancel if @active_1xx_timer
890
+ else
891
+ logd("PRACK doesnt match the last sent reliable response.")
892
+ end
893
+ end
894
+ end
895
+
896
+ if request.method == "INVITE"
897
+ if self.use_ist
898
+ branch = request.via.branch
899
+ ist = @transactions[branch]
900
+ if ist
901
+ logd("Found transaction #{ist} for branch #{request.via.branch} for INVITE retransmission")
902
+ else
903
+ # one final check for equal CSeq since this is not a retransmission now and therefore
904
+ # must be a bad request.
905
+ _equal_cseq_check(request) unless request.attributes[:_sipper_rejection_response]
906
+ # check for existing invite transactions
907
+ _check_pending_invite_txns_on_invite_in(request)unless request.attributes[:_sipper_rejection_response]
908
+ logd("Not found transaction for branch #{request.via.branch} for INVITE, creating a new IST")
909
+ klass = @transaction_handlers[:Ist] || @transaction_handlers[:Base]
910
+ txn_handler = klass.new if klass
911
+ ist = SIP::Transaction::InviteServerTransaction.new(self, branch, txn_handler, transport, @tp_flags)
912
+ @tmr_hash[:Ist].each_pair {|k,v| ist.send("#{k}=".to_sym, v) }
913
+ @transactions[branch] = ist
914
+ end
915
+ request.transaction = ist
916
+ ist.txn_received(request)
917
+ unless ist.consume?
918
+ logd("Not consuming this request #{request.method} as txn has taken care of it.")
919
+ return
920
+ end
921
+ end # ist being used
922
+ elsif request.method == "ACK"
923
+ txn = @transactions[request.via.branch]
924
+ if txn
925
+ logd("Found transaction for branch #{request.via.branch} for ACK")
926
+ request.transaction = txn
927
+ txn.txn_received(request)
928
+ unless txn.consume?
929
+ logd("Not consuming this request #{request.method} as txn has taken care of it.")
930
+ return
931
+ end
932
+ else # no transaction found, this must be a ACK for 2xx as new branch
933
+ @ok_to_retrans_2xx = false
934
+ end
935
+ else # method other than INV/ACK
936
+ if self.use_nist
937
+ branch = request.via.branch
938
+ stxn = @transactions[branch]
939
+ if request.method == "CANCEL"
940
+ if stxn
941
+ nist = stxn.cancel_stxn
942
+ logd("Found CANCEL transaction #{nist} for branch #{request.via.branch} for CANCEL retransmission") if nist
943
+ end
944
+ else
945
+ nist = stxn
946
+ logd("Found NIST transaction #{nist} for branch #{request.via.branch} for non-INVITE retransmission") if nist
947
+ end
948
+ unless nist
949
+ # one final check for equal CSeq since this is not a retransmission now and therefore
950
+ # must be a bad request, unless it is a CANCEL.
951
+ unless request.method == "CANCEL"
952
+ _equal_cseq_check(request) unless request.attributes[:_sipper_rejection_response]
953
+ end
954
+ logd("Not found transaction for branch #{request.via.branch} for non-INVITE, creating a new NIST")
955
+ klass = @transaction_handlers[:Nist] || @transaction_handlers[:Base]
956
+ txn_handler = klass.new if klass
957
+ nist = SIP::Transaction::NonInviteServerTransaction.new(self, branch, txn_handler, transport, @tp_flags)
958
+ @tmr_hash[:Nist].each_pair {|k,v| nist.send("#{k}=".to_sym, v) }
959
+ end
960
+ request.transaction = nist
961
+ if request.method == "CANCEL"
962
+ logd("Received CANCEL found a Stx #{stxn} for branch #{branch}")
963
+ if stxn
964
+ nist.transaction_being_canceled = stxn if nist
965
+ forward_cancel_to_stxn = true
966
+ else
967
+ nist.ok_to_send_481_to_cancel = true if self.use_ist && self.use_nist
968
+ logd("Processing CANCEL, no stx found, check if 481 is to be sent")
969
+ end
970
+ else
971
+ @transactions[branch] = nist unless stxn
972
+ end
973
+ nist.txn_received(request)
974
+ unless nist.consume?
975
+ logd("Not consuming this request #{request.method} as txn has taken care of it.")
976
+ return
977
+ end
978
+ else # nist not being used, but still look for IST for CANCEL
979
+ if request.method == "CANCEL"
980
+ branch = request.via.branch
981
+ stxn = @transactions[branch] # should be IST
982
+ logd("Received CANCEL found a Stx #{stxn} for branch #{branch}")
983
+ if stxn
984
+ forward_cancel_to_stxn = true
985
+ else
986
+ logd("Processing CANCEL, no stx found, not using nist either")
987
+ end
988
+ end
989
+ end
990
+ end
991
+
992
+ # Check for the rejection response here
993
+ if request.attributes[:_sipper_rejection_response]
994
+ logi("Found the rejection response, sending it and not invoking controller")
995
+ send_response(request.attributes[:_sipper_rejection_response])
996
+ return
997
+ end
998
+
999
+ if request.method == "REGISTER"
1000
+ @registrations = _create_registration_object(request)
1001
+ end
1002
+
1003
+ if @controller
1004
+ logd("Dispatching request to controller #{@controller.name}")
1005
+ begin
1006
+ result = @controller.on_request(self)
1007
+ rescue Exception => e
1008
+ loge("Exception #{e} occured while request processing by controller")
1009
+ loge(e.backtrace.join("\n"))
1010
+ end
1011
+ logw("#{@controller} could not process the request") if result == false
1012
+ else
1013
+ loge("No controller associated with this session")
1014
+ end
1015
+ stxn.cancel_received(request, nist) if forward_cancel_to_stxn && stxn
1016
+ end
1017
+
1018
+
1019
+ def pre_set_dialog_id(r)
1020
+ @remote_tag = r.from_tag
1021
+ @call_id = r.call_id.to_s
1022
+ end
1023
+
1024
+ # invoked on incoming response
1025
+ def _on_response(response)
1026
+ if ((SipperConfigurator[:ProtocolCompliance]=='strict') && !(response.locally_generated?))
1027
+ # check 18.1.2
1028
+ # When a response is received, the client transport examines the top Via header field value.
1029
+ # If the value of the "sent-by" parameter in that header field value does not
1030
+ # correspond to a value that the client transport is configured to insert
1031
+ # into requests, the response MUST be silently discarded.
1032
+ if (transport.ip != response.via.sent_by_ip) || (transport.port.to_s != response.via.sent_by_port) && SipperConfigurator[:ProtocolCompliance] == 'strict'
1033
+ logw("Response via #{response.via} sent_by does not match our transport, dropping message")
1034
+ return
1035
+ end
1036
+ end
1037
+ _update_dialog_state_on_response(response)
1038
+ @offer_answer.handle_incoming_response(response) if @offer_answer
1039
+ txn = @transactions[response.via.branch]
1040
+ if txn && !(response.locally_generated?) && response.get_request_method == "CANCEL"
1041
+ txn = txn.cancel_ctxn || txn # for the case when NICT but not ICT in use
1042
+ end
1043
+ if (txn && !(response.locally_generated?))
1044
+ logd("Found transaction #{txn} for branch #{response.via.branch} giving it the response")
1045
+ response.transaction = txn
1046
+ txn.txn_received(response)
1047
+ unless txn.consume?
1048
+ logd("Not consuming this response #{response.code} as txn has taken care of it.")
1049
+ return
1050
+ end
1051
+ end
1052
+
1053
+ if response.get_request_method == "REGISTER"
1054
+ @registrations = _create_registration_object(response, false)
1055
+ end
1056
+
1057
+ if @controller
1058
+ begin
1059
+ @controller.on_response(self)
1060
+ rescue Exception => e
1061
+ loge("Exception #{e} occured while response processing by controller")
1062
+ loge(e.backtrace.join("\n"))
1063
+ end
1064
+ else
1065
+ loge("No controller associated with this session")
1066
+ end
1067
+ end
1068
+
1069
+ # On receipt of request or response
1070
+ def _on_common_sip(msg)
1071
+ if msg[:content_type] && msg.content_type.to_s =~ /sdp/
1072
+ msg.sdp = SDP::SdpParser.parse(msg.contents, true) if msg[:content]
1073
+ end
1074
+ @call_id = msg.call_id.to_s
1075
+ logd("Now record the incoming message")
1076
+ _do_record_sip("in", msg)
1077
+ end
1078
+
1079
+ #-------------------------------
1080
+ # Transcation handling setting
1081
+
1082
+ def use_ict
1083
+ return @use_ict unless @use_ict.nil?
1084
+ @use_transactions
1085
+ end
1086
+
1087
+ def use_nict
1088
+ return @use_nict unless @use_nict.nil?
1089
+ @use_transactions
1090
+ end
1091
+
1092
+ def use_ist
1093
+ return @use_ist unless @use_ist.nil?
1094
+ @use_transactions
1095
+ end
1096
+
1097
+ def use_nist
1098
+ return @use_nist unless @use_nist.nil?
1099
+ @use_transactions
1100
+ end
1101
+
1102
+ def set_transaction_usage(tr_hash)
1103
+ tr_hash.each_pair { |k,v| self.__send__((k.to_s+'=').to_sym, v) } if tr_hash
1104
+ end
1105
+
1106
+ # returns the consolidated hash of timers which can be set to the
1107
+ # transaction in question by calling SipperUtil.hash_to_iv(@timers, txn) if @timers
1108
+ # the type of transactions can be either the names of transactions as @transation_name
1109
+ # defined in the transaction or it can be :Base which is the override for all types of
1110
+ # transactions.
1111
+ def set_transaction_timers(type, tmr_hash)
1112
+ if tmr_hash
1113
+ if type == :Base
1114
+ @tmr_hash.each_key {|k| @tmr_hash[k]= tmr_hash }
1115
+ else
1116
+ @tmr_hash[type] = @tmr_hash[type].merge(tmr_hash)
1117
+ end
1118
+ end
1119
+ #@tmr_hash.each_key {|k| puts "#{k} = #{@tmr_hash[k]}" }
1120
+ end
1121
+
1122
+ # e.g. :Ict=>MyIctHandler, :Nict=>MyNictHandler, :Base=>CatchAllHandler
1123
+ def set_transaction_handlers(tx_hnd_hash)
1124
+ @transaction_handlers = tx_hnd_hash if tx_hnd_hash
1125
+ end
1126
+
1127
+ def set_session_timer(val)
1128
+ @session_timer = val if val
1129
+ end
1130
+
1131
+ def set_session_limit(val)
1132
+ @session_limit = val if val
1133
+ end
1134
+
1135
+
1136
+ def set_session_record(val)
1137
+ @session_record = val
1138
+ end
1139
+
1140
+ # boolean, whether the 2xx retransmission timer is to be used or not for
1141
+ # the UAS
1142
+ def set_t2xx_retrans_usage(val)
1143
+ @use_2xx_retrans = val
1144
+ end
1145
+
1146
+ def set_t1xx_retrans_usage(val)
1147
+ @use_1xx_retrans = val
1148
+ end
1149
+
1150
+ # the hash of 3 timer values that affect 2xx retransmissions for UAS
1151
+ # i.e {:Start=>100, :Cap=>400, :Limit=>1600} that roughly
1152
+ # correspond with T1, T2 and 64*T1 respectively, which are also the
1153
+ # defaults.
1154
+ def set_t2xx_retrans_timers(t_hash)
1155
+ if t_hash
1156
+ @t2xx_retrans_timers = @t2xx_retrans_timers.merge t_hash
1157
+ end
1158
+ end
1159
+
1160
+ def set_t1xx_retrans_timers(t_hash)
1161
+ if t_hash
1162
+ @t1xx_retrans_timers = @t1xx_retrans_timers.merge t_hash
1163
+ end
1164
+ end
1165
+
1166
+ # The partial or full list of headers in the order in which the message
1167
+ # is to be formatted. In the absense of this the header ordering is
1168
+ # arbitrary. e.g.
1169
+ # set_header_order([:from, :to, :call_id, :via])
1170
+ # shall format the headers in the order
1171
+ # ....
1172
+ # From: sut <sip:service@127.0.0.1:5060>;tag=azxs21
1173
+ # To: sipp <sip:sipp@127.0.0.1:6061>;tag=1
1174
+ # Call-Id: 6766_112@127.0.0.1
1175
+ # Via: SIP/2.0/UDP 127.0.0.1:6061;branch=z9hG4bK-2352-1-0
1176
+ # ....
1177
+ # once set this setting affects all the messages created by this session.
1178
+ def set_header_order(arr)
1179
+ @header_order_arr = arr
1180
+ end
1181
+ #------------------------------
1182
+
1183
+ # The headers which are required to be expressed in their compact form.
1184
+ # e.g.
1185
+ # set_compact_headers [:from, :via]
1186
+ # shall use the compact form for headers From and Via and the message shall
1187
+ # look like -
1188
+ # f: sut <sip:service@127.0.0.1:5060>;tag=azxs21
1189
+ # To: sipp <sip:sipp@127.0.0.1:6061>;tag=1
1190
+ # Call-Id: 6766_112@127.0.0.1
1191
+ # v: SIP/2.0/UDP 127.0.0.1:6061;branch=z9hG4bK-2352-1-0
1192
+ #
1193
+ # In case all possible headers are required in compact form then use
1194
+ # [:all_headers] as the argument value.
1195
+ # A complete list of headers with known compact form can be obtained
1196
+ # from - http://www.iana.org/assignments/sip-parameters
1197
+ def set_compact_headers(arr)
1198
+ @compact_header_arr = arr
1199
+ end
1200
+
1201
+ # public method called when the timer of this session target is fired
1202
+ def on_timer_expiration(task)
1203
+ # we treat the incoming timer task as a message to leverage
1204
+ # the session level queueing for synchronization.
1205
+ on_message(task)
1206
+ end
1207
+
1208
+ # this is internally called when the timer is acted upon
1209
+ def _on_timer(task)
1210
+ if @invalidated
1211
+ logi("This session #{self.session_key} is invalidated, not firing timer #{task}")
1212
+ return
1213
+ end
1214
+ logd("Timer task #{task} invoked")
1215
+ if task.type == :app
1216
+ begin
1217
+ @controller.on_timer(self, task) if @controller
1218
+ rescue Exception => e
1219
+ loge("Exception #{e} occured while timer processing by controller")
1220
+ loge(e.backtrace.join("\n"))
1221
+ end
1222
+ elsif task.type == :subscription
1223
+ begin
1224
+ subscription = task.tid
1225
+ if subscription.state != 'active'
1226
+ return
1227
+ end
1228
+ if task.equal?subscription.timer
1229
+ if subscription.source == 'uac'
1230
+ @controller.on_subscription_refresh_timeout(self, subscription)
1231
+ else
1232
+ @controller.on_subscription_timeout(self, subscription)
1233
+ end
1234
+ end
1235
+ end
1236
+ elsif task.type == :registration
1237
+ registration = task.tid
1238
+ begin
1239
+ @controller.on_registration_expiry(self, registration)
1240
+ end
1241
+ elsif task.type == :session
1242
+ if task.tid == :session_timer
1243
+ if @controller
1244
+ logd("Invoking session listener for #{@controller.name}")
1245
+ begin
1246
+ result = @controller.session_being_invalidated_ok_to_proceed?(self)
1247
+ rescue Exception => e
1248
+ loge("Exception #{e} occured while callback processing by controller")
1249
+ loge(e.backtrace.join("\n"))
1250
+ end
1251
+ if result
1252
+ logd("#{@controller} not interested in session invalidation")
1253
+ else
1254
+ logd("#{@controller} decided to increase the lifetime of session")
1255
+ if (@session_life_so_far+@session_timer < @session_limit)
1256
+ @invalidating = false # to force the timer to start
1257
+ self.invalidate
1258
+ return
1259
+ else
1260
+ logw("Not increasing the lifetime of session, as upper session limit reached")
1261
+ end
1262
+ end
1263
+ else
1264
+ logd("No controller interested in session invalidation")
1265
+ end
1266
+ self.invalidate(true)
1267
+ elsif task.tid == :t2xx_timer
1268
+ logd("2xx retransmission timer fired for #{self}")
1269
+ if @ok_to_retrans_2xx
1270
+ _do_record_sip("out", @two_xx.response, @two_xx.response.to_s)
1271
+ transport.send(@two_xx.response, @two_xx.tp_flags, @two_xx.rip, @two_xx.rp)
1272
+ logd("Retransmitted the 2xx response from #{self}")
1273
+ @current_t2xx = [@t2xx_retrans_timers[:Start]*2, @t2xx_retrans_timers[:Cap]].min
1274
+ _schedule_timer_for_session(:t2xx_timer, @current_t2xx)
1275
+ end # still ok to retrans
1276
+ elsif task.tid == :t2xx_limit_timer
1277
+ @ok_to_retrans_2xx = false
1278
+ if @controller
1279
+ logd("Invoking no_ack_received session listener for #{@controller.name}")
1280
+ begin
1281
+ result = @controller.no_ack_received(self)
1282
+ rescue Exception => e
1283
+ loge("Exception #{e} occured while callback processing by controller")
1284
+ loge(e.backtrace.join("\n"))
1285
+ end
1286
+ end # if controller
1287
+ elsif task.tid == :t1xx_timer
1288
+ if task.equal?@active_1xx_timer
1289
+ logd("1xx retransmission timer fired for #{self}")
1290
+ if @ok_to_retrans_1xx
1291
+ _do_record_sip("out", @one_xx.response, @one_xx.response.to_s)
1292
+ transport.send(@one_xx.response, @one_xx.tp_flags, @one_xx.rip, @one_xx.rp)
1293
+ logd("Retransmitted the 1xx response from #{self}")
1294
+ @current_t1xx = @current_t1xx*2
1295
+ @active_1xx_timer.cancel if @active_1xx_timer
1296
+ @active_1xx_timer = _schedule_timer_for_session(:t1xx_timer, @current_t1xx)
1297
+ end # still ok to retrans
1298
+ else
1299
+ logd("Ignoring invalid 1xx timer #{task}")
1300
+ end
1301
+ elsif task.tid == :t1xx_limit_timer
1302
+ @ok_to_retrans_1xx = false
1303
+ if @controller
1304
+ logd("Invoking no_prack_received session listener for #{@controller.name}")
1305
+ begin
1306
+ result = @controller.no_prack_received(self)
1307
+ rescue Exception => e
1308
+ loge("Exception #{e} occured while callback processing by controller")
1309
+ loge(e.backtrace.join("\n"))
1310
+ end
1311
+ end # if controller
1312
+ elsif task.tid == :session_limit
1313
+ logi("Upper limit of session time limit reached, now invalidating #{self}")
1314
+ self.invalidate(true)
1315
+ end #type of session timer
1316
+ end #session or app
1317
+ end
1318
+
1319
+
1320
+ def schedule_timer_for(tid, duration, &block)
1321
+ logd("Scheduling an app timer #{tid} for #{duration}")
1322
+ SIP::Locator[:Sth].schedule_for(self, tid, block, :app, duration)
1323
+ end
1324
+
1325
+ def _schedule_timer_for_session(tid, duration, &block)
1326
+ logd("Scheduling a session level timer #{tid} for #{duration}")
1327
+ SIP::Locator[:Sth].schedule_for(self, tid, block, :session, duration)
1328
+ end
1329
+
1330
+ def send_http_post_to(url, params)
1331
+ SIP::Locator[:HttpRequestDispatcher].request_post(url, self, params)
1332
+ end
1333
+
1334
+ def send_http_get_to(url)
1335
+ SIP::Locator[:HttpRequestDispatcher].request_get(url, self)
1336
+ end
1337
+
1338
+ # Invoked when the HTTP response is ready to be consumed
1339
+ def on_http_response(http_res)
1340
+ on_message(http_res)
1341
+ end
1342
+
1343
+ # Invoked when either the Sipper media response or an actual media event
1344
+ # is received.
1345
+ def on_media_event(media_evt)
1346
+ # we treat the incoming media event as a message to leverage
1347
+ # the session level queueing for synchronization.
1348
+ on_message(media_evt)
1349
+ end
1350
+
1351
+ def _on_media_event(media_event)
1352
+ @imedia_event = media_event
1353
+
1354
+ if @controller
1355
+ logd("Dispatching media event to controller #{@controller.name}")
1356
+ begin
1357
+ result = @controller.on_media_event(self)
1358
+ rescue Exception => e
1359
+ loge("Exception #{e} occured while media processing by controller")
1360
+ loge(e.backtrace.join("\n"))
1361
+ end
1362
+ logw("#{@controller} could not process the media event") if result == false
1363
+ else
1364
+ loge("No controller associated with this session")
1365
+ end
1366
+ end
1367
+
1368
+ def _on_http_response(http_res)
1369
+ @ihttp_response = http_res
1370
+ if @controller
1371
+ logd("Dispatching http response to controller #{@controller.name}")
1372
+ begin
1373
+ result = @controller.on_http_res(self)
1374
+ rescue Exception => e
1375
+ loge("Exception #{e} occured while http response processing by controller")
1376
+ loge(e.backtrace.join("\n"))
1377
+ end
1378
+ logw("#{@controller} could not process the http response") if result == false
1379
+ else
1380
+ loge("No controller associated with this session")
1381
+ end
1382
+ end
1383
+
1384
+ def _on_custom_message(custom_msg)
1385
+ if @controller
1386
+ logd("Dispatching custom message to controller #{@controller.name}")
1387
+ begin
1388
+ result = @controller.on_custom_msg(self, custom_msg)
1389
+ rescue Exception => e
1390
+ loge("Exception #{e} occured while processing custom message by controller")
1391
+ loge(e.backtrace.join("\n"))
1392
+ end
1393
+ logw("#{@controller} could not process the custom message") if result == false
1394
+ else
1395
+ loge("No controller associated with this session")
1396
+ end
1397
+ end
1398
+
1399
+ def post_custom_message(custom_msg)
1400
+ on_message(custom_msg)
1401
+ end
1402
+
1403
+
1404
+ def invalidate(force=false)
1405
+ if @invalidated
1406
+ logw("This session with key #{self.session_key} is already invalidated")
1407
+ return
1408
+ end
1409
+
1410
+ if force
1411
+ logd("Now invalidating the session #{self} with key #{self.session_key}")
1412
+ @offer_answer.close if @offer_answer
1413
+ @transactions.each_value do |t|
1414
+ logd("Invalidating the txn #{t} in session #{self}")
1415
+ t.invalidate
1416
+ end
1417
+ @session_recorder.save if @session_recorder
1418
+ SessionManager.remove_session self
1419
+ @invalidated = true
1420
+ SIP::TestCompletionSignalingHelper.signal_waiting_test(@signal_test_complete_when_invalidated) if @signal_test_complete_when_invalidated
1421
+ else
1422
+ if @invalidating
1423
+ logi("This session with key #{self.session_key} is already scheduled for invalidation")
1424
+ return
1425
+ end
1426
+ @invalidating = true
1427
+ tmr = _schedule_timer_for_session(:session_timer, @session_timer)
1428
+ @session_life_so_far += @session_timer
1429
+ logd("Now scheduling the session #{self.session_key} for invalidation after #{@session_timer} #{tmr}")
1430
+ end
1431
+ end
1432
+
1433
+
1434
+ def record_io=(rio)
1435
+ @rio = rio
1436
+ end
1437
+
1438
+ def do_record(msg)
1439
+ msg = SipperUtil.recordify(msg)
1440
+ _do_record_sip("neutral", msg)
1441
+ end
1442
+
1443
+ def transaction_record(direction, msg)
1444
+ _do_record_sip(direction, msg)
1445
+ end
1446
+
1447
+ def flow_completed_for(test_name)
1448
+ unless @invalidated
1449
+ logd("Session not invalidated yet, setting flag signaling completion")
1450
+ @signal_test_complete_when_invalidated = test_name.to_s
1451
+ return true
1452
+ end
1453
+ logd("In flow_completed_for() now siganling the waiting test")
1454
+ SIP::TestCompletionSignalingHelper.signal_waiting_test(test_name.to_s)
1455
+ end
1456
+
1457
+ # The first recording will create the recording file name using in or out
1458
+ # todo will it be enough for uniqueness?
1459
+ def _do_record_sip(direction, msg, msg_s=nil)
1460
+ logd("record() invoked for #{direction} and #{msg.call_id.to_s}")
1461
+ # maintain simple state in the session
1462
+ unless @user_defined_state
1463
+ if direction == "out"
1464
+ if msg.is_request?
1465
+ @state << ("sent_" + msg.method.downcase)
1466
+ elsif msg.is_response?
1467
+ @state << ("sent_" + msg.code.to_s)
1468
+ end
1469
+ elsif direction == "in"
1470
+ if msg.is_request?
1471
+ @state << ("received_" + msg.method.downcase)
1472
+ elsif msg.is_response?
1473
+ @state << ("received_" + msg.code.to_s)
1474
+ end
1475
+ end
1476
+ end
1477
+ unless @session_recorder
1478
+ @session_recorder = SessionRecorder.create_and_record(@rio, msg, msg_s, direction, @session_record)
1479
+ else
1480
+ @session_recorder.record(direction, msg, msg_s)
1481
+ end
1482
+ rescue RuntimeError => e
1483
+ loge("Unable to record the #{msg} as recorder is closed")
1484
+ end
1485
+
1486
+
1487
+ def _check_cancel_state(msg)
1488
+ @cancellable_txns.include? msg.txn_id
1489
+ end
1490
+
1491
+ def _check_for_pending_cancel
1492
+ txn_id = @iresponse.txn_id
1493
+ @cancellable_txns << txn_id
1494
+ if pc = @pending_cancels[txn_id]
1495
+ @pending_cancels[txn_id] = nil
1496
+ if SipperConfigurator[:ProtocolCompliance] == 'strict'
1497
+ return if @iresponse.code >= 200
1498
+ end
1499
+ send pc
1500
+ end
1501
+ end
1502
+
1503
+ def _increment_local_cseq
1504
+ @local_cseq_before_send ||= @local_cseq
1505
+ @local_cseq += 1
1506
+ logd "Local cseq before send set to #{@local_cseq_before_send} and @local_cseq is #{@local_cseq}"
1507
+ end
1508
+
1509
+ # Useful when you create a subsequent request but do not send it.
1510
+ # This resets the state to the time when the request was not created.
1511
+ def rollback_to_unsent_state
1512
+ @local_cseq = @local_cseq_before_send if @local_cseq_before_send
1513
+ @local_cseq_before_send = nil
1514
+ end
1515
+
1516
+ # 3261 Section 8.2
1517
+ # Note that request processing is atomic.
1518
+ # If a request is accepted, all state changes associated with it MUST be performed.
1519
+ # If it is rejected, all state changes MUST NOT be performed.
1520
+ #
1521
+ # This rollback is invoked automatically when Sipper rejects the request before it invokes
1522
+ # the controller. The controller can invoke it to rollback any dialog state if it is
1523
+ # going to reject the request or alternatively if the compliance flag is set to strict
1524
+ # this will automatically be called on request rejection.
1525
+ # Note that the snapshot is single-depth state capture, does not save any contained
1526
+ # object's state.
1527
+ def rollback_to_before_request_received_state(request)
1528
+ if request.session_state_snapshot
1529
+ self.restore_snapshot(request.session_state_snapshot)
1530
+ @dialog_routes.restore_snapshot(@dialog_routes_snap)
1531
+ @dialog_routes_snap = nil
1532
+ request.session_state_snapshot = nil
1533
+ logd("Restored to previous session state for request #{request.method}")
1534
+ else
1535
+ logd("No state to rollback.")
1536
+ end
1537
+ end
1538
+
1539
+ # creates a failure response and also rolls back the session state, this is called
1540
+ # when the request is rejected by Sipper itself.
1541
+ def rejection_response_with(code, request)
1542
+ logi("Rejecting the request #{request.method} with a #{code}")
1543
+ r = create_response(code, "SELECT", request)
1544
+ rollback_to_before_request_received_state(request)
1545
+ return r
1546
+ end
1547
+
1548
+ # Sometimes you create a new session but would like to continue the
1549
+ # recording process from previous session for validation purposes.
1550
+ # This would typically be used when you drop a leg and create a new
1551
+ # one.
1552
+ def continue_recording_from(old_session)
1553
+ @session_recorder = old_session._get_recorder
1554
+ old_session._remove_recorder
1555
+ end
1556
+
1557
+
1558
+ def _update_and_check_dialog_state_on_request(request)
1559
+ new_remote_cseq = SipperUtil.cseq_number(request.cseq)
1560
+ @dialog_routes_snap = @dialog_routes.take_snapshot
1561
+ request.session_state_snapshot = self.take_snapshot
1562
+ @dialog_routes.request_received(request)
1563
+ _on_common_sip(request)
1564
+ @imessage = @irequest = request
1565
+ @remote_tag = @irequest.from_tag unless @remote_tag
1566
+ @local_uri = @irequest.to.to_s
1567
+ @remote_uri = @irequest.from.to_s
1568
+
1569
+ # 3261 12.2.2 testing for < because a retransmission will have same sequence, as will be
1570
+ # ACK and CANCEL
1571
+ if SipperConfigurator[:ProtocolCompliance]=='strict' &&
1572
+ new_remote_cseq < @remote_cseq
1573
+ if @irequest.method == "ACK"
1574
+ logd("Dropping an ACK for an out of CSeq rejected request")
1575
+ else
1576
+ request.attributes[:_sipper_rejection_response] = rejection_response_with(500, @irequest)
1577
+ logd("Rejected the request #{@irequest.method} for out of CSeq")
1578
+ end
1579
+ return false
1580
+ end
1581
+ request.attributes[:_sipper_old_cseq] = @remote_cseq # save old for later check
1582
+ @remote_cseq = new_remote_cseq
1583
+
1584
+ if request.method == 'NOTIFY'
1585
+ if @session_map == :half
1586
+ if get_subscription(request) != nil
1587
+ logd("Moving the dialog to full on Subscription notify.")
1588
+ SessionManager.find_session(request.call_id, request.to_tag, request.from_tag, true)
1589
+ end
1590
+ end
1591
+ end
1592
+ return true
1593
+ end
1594
+
1595
+ def _update_dialog_state_on_response(response)
1596
+ @dialog_routes.response_received(response)
1597
+ _on_common_sip(response)
1598
+ @imessage = @iresponse = response
1599
+ _check_for_pending_cancel
1600
+ @remote_tag = response.to_tag
1601
+ @remote_uri = response.to.to_s
1602
+ end
1603
+
1604
+ # Reject the request if we have seen thsi CSeq before and there is no transaction
1605
+ # found for this request which rules out a retransmission.
1606
+ def _equal_cseq_check(request)
1607
+ if SipperConfigurator[:ProtocolCompliance] == 'strict' &&
1608
+ SipperUtil.cseq_number(request.cseq) == request.attributes[:_sipper_old_cseq]
1609
+ request.attributes[:_sipper_rejection_response] = rejection_response_with(500, @irequest)
1610
+ logd("Rejected the request #{request.method} for out of (equal) CSeq")
1611
+ false
1612
+ else
1613
+ true
1614
+ end
1615
+ end
1616
+
1617
+ # A UAS that receives a second INVITE before it sends the final response to
1618
+ # a first INVITE with a lower CSeq sequence number on the same dialog MUST
1619
+ # return a 500 (Server Internal Error) response to the second INVITE and
1620
+ # MUST include a Retry-After header field with a randomly chosen value
1621
+ # of between 0 and 10 seconds.
1622
+ #
1623
+ # A UAS that receives an INVITE on a dialog
1624
+ # while an INVITE it had sent on that dialog is in progress MUST return a
1625
+ # 491 (Request Pending) response to the received INVITE.
1626
+ def _check_pending_invite_txns_on_invite_in(request)
1627
+ @transactions.values.each do |txn|
1628
+ if txn.transaction_name == :Ict
1629
+ unless ["IctMap.Completed", "IctMap.Terminated"].include? txn.state
1630
+ request.attributes[:_sipper_rejection_response] = rejection_response_with(491, request)
1631
+ return false
1632
+ end
1633
+ elsif txn.transaction_name == :Ist
1634
+ unless ["IstMap.Confirmed", "IstMap.Terminated", "IstMap.Finished"].include? txn.state
1635
+ r = rejection_response_with(500, request)
1636
+ r.retry_after = rand(10).to_s
1637
+ request.attributes[:_sipper_rejection_response] = r
1638
+ return false
1639
+ end
1640
+ end
1641
+ end
1642
+ return true
1643
+ end
1644
+
1645
+
1646
+ def _add_route_headers_if_present(msg, rrt)
1647
+ unless rrt[1].empty?
1648
+ msg.route = rrt[1]
1649
+ msg.format_as_separate_headers_for_mv(:route)
1650
+ end
1651
+ msg.attributes[:_sipper_use_ruri_to_send] = rrt[2]
1652
+ msg
1653
+ end
1654
+
1655
+
1656
+ # populates the transport, rip and rp attributes in the session if they are not
1657
+ # set. This can happen if an unbound session is created.
1658
+ def _check_transport_and_destination(msg)
1659
+ if !(@transport && @rip && @rp) || @dialog_routes.target_refreshed?
1660
+ if (@controller && (cstp=@controller.specified_transport))
1661
+ msg.attributes[:_sipper_controller_specified_transport] = cstp
1662
+ end
1663
+ rv = Transport::TransportAndRouteResolver.ascertain_transport_and_destination(msg)
1664
+ @transport = rv[0]||@transport
1665
+ @rip = rv[1] || @rip
1666
+ @rp = rv[2] || @rp
1667
+ end
1668
+ end
1669
+
1670
+ def _get_sq_lock
1671
+ @sq_lock
1672
+ end
1673
+
1674
+ def _get_recorder
1675
+ @session_recorder
1676
+ end
1677
+
1678
+ def _remove_recorder
1679
+ @session_recorder = nil
1680
+ end
1681
+
1682
+ def create_subscription_from_request(request)
1683
+ # Creating subscription based on Subscribe/Refer request received.
1684
+ event = request.event.header_value if request["event".to_sym]
1685
+
1686
+ unless event
1687
+ if request.method == "REFER"
1688
+ event = "refer"
1689
+ else
1690
+ log_and_raise "Invalid subscription", ArgumentError
1691
+ end
1692
+ end
1693
+
1694
+ if event == "refer" && request.method != "REFER"
1695
+ log_and_raise "Only Refer method can create refer subscription", ArgumentError
1696
+ end
1697
+
1698
+ id_val = request.event['id'] if request["event".to_sym]
1699
+ unless id_val
1700
+ id_val = 0
1701
+ end
1702
+
1703
+ key = sprintf("|%s|%d", event, id_val)
1704
+ subscription = @subscriptionMap[key]
1705
+ unless subscription
1706
+ logd("Creating new uas subscription object.")
1707
+ subscription = SubscriptionData.new
1708
+ subscription.key = key
1709
+ subscription.timer = nil
1710
+ subscription.source = 'uas'
1711
+ subscription.event = event
1712
+ subscription.event_id = id_val
1713
+ subscription.state = 'active'
1714
+ subscription.method = request.method
1715
+
1716
+ if request.method == "SUBSCRIBE"
1717
+ if request.expires.header_value == '0'
1718
+ subscription.state = "terminated"
1719
+ end
1720
+ end
1721
+ @subscriptionMap[key] = subscription
1722
+ end
1723
+
1724
+ return @subscriptionMap[key]
1725
+ end
1726
+
1727
+ def create_subscription(event, id_val=0, method="SUBSCRIBE")
1728
+ # Creating new subscription.
1729
+ key = sprintf("|%s|%d", event, id_val)
1730
+ subscription = @subscriptionMap[key]
1731
+ unless subscription
1732
+ logd("Creating new uac subscription object.")
1733
+ subscription = SubscriptionData.new
1734
+ subscription.key = key
1735
+ subscription.timer = nil
1736
+ subscription.source = 'uac'
1737
+ subscription.event = event
1738
+ subscription.event_id = id_val
1739
+ subscription.state = 'active'
1740
+ subscription.method = method
1741
+ @subscriptionMap[key] = subscription
1742
+ end
1743
+
1744
+ return subscription
1745
+ end
1746
+
1747
+ def get_subscription(request)
1748
+ event = request.event.header_value if request["event".to_sym]
1749
+
1750
+ unless event
1751
+ if request.method == "REFER"
1752
+ event = "refer"
1753
+ else
1754
+ log_and_raise "Invalid subscription", ArgumentError
1755
+ end
1756
+ end
1757
+
1758
+ event_id = request.event['id'] if request["event".to_sym]
1759
+ unless event_id
1760
+ event_id = 0
1761
+ end
1762
+
1763
+ key = sprintf("|%s|%d", event, event_id)
1764
+ subscription = @subscriptionMap[key]
1765
+
1766
+ return subscription
1767
+ end
1768
+
1769
+ def update_subscription(request)
1770
+ subscription = get_subscription(request)
1771
+
1772
+ if request.method == "NOTIFY"
1773
+ subscription.state = request.subscription_state.header_value
1774
+
1775
+ if request.subscription_state["expires"] != nil
1776
+ if request.subscription_state["expires"] == '0'
1777
+ subscription.state = "terminated"
1778
+ end
1779
+ end
1780
+ end
1781
+
1782
+ if request.method == "SUBSCRIBE"
1783
+ if request.expires.header_value == '0'
1784
+ subscription.state = "terminated"
1785
+ end
1786
+ end
1787
+
1788
+ return subscription
1789
+ end
1790
+
1791
+ def remove_subscription(request)
1792
+ event = request.event.header_value if request["event".to_sym]
1793
+
1794
+ unless event
1795
+ if request.method == "REFER"
1796
+ event = "refer"
1797
+ else
1798
+ log_and_raise "Invalid subscription", ArgumentError
1799
+ end
1800
+ end
1801
+
1802
+ event_id = request.event.id if request["event".to_sym]
1803
+ unless event_id
1804
+ event_id = 0
1805
+ end
1806
+
1807
+ key = sprintf("|%s|%d", event, event_id)
1808
+ @subscriptionMap[key] = nil
1809
+ end
1810
+
1811
+ def remove_subscription(subscription)
1812
+ @subscriptionMap[subscription.key] = nil
1813
+ end
1814
+
1815
+ def add_subscription_to_request(request, subscription)
1816
+ logd(subscription.to_s)
1817
+
1818
+ if subscription.event != "refer" || request.method != "REFER" || subscription.event_id != 0
1819
+ request.event = subscription.event
1820
+ end
1821
+
1822
+ if subscription.event_id != 0
1823
+ request.event.id = subscription.event_id.to_s
1824
+ end
1825
+
1826
+ if request.method == "NOTIFY"
1827
+ request['subscription-state'] = subscription.state
1828
+ end
1829
+ end
1830
+
1831
+ def _schedule_timer_for_subscription(duration, subscription)
1832
+ logd("Scheduling a subscription timer for #{duration}")
1833
+ subscription.timer = SIP::Locator[:Sth].schedule_for(self, subscription, nil, :subscription, duration)
1834
+ end
1835
+
1836
+ def start_subscription_expiry_timer(subscription, response)
1837
+ logd('starting subscription expiry timer')
1838
+ if subscription.state != "active"
1839
+ logd("Not scheduling timer as state is not active")
1840
+ return
1841
+ end
1842
+
1843
+ expires = response.expires.header_value
1844
+ if expires == nil
1845
+ logd("Not scheduling timer as expires is empty")
1846
+ return
1847
+ end
1848
+
1849
+ expiresDuration = expires.to_i * 1000
1850
+
1851
+ logd("Scheduling timer for subscription")
1852
+ _schedule_timer_for_subscription(expiresDuration, subscription)
1853
+ end
1854
+
1855
+ def start_subscription_refresh_timer(subscription, response)
1856
+ if subscription.state != "active"
1857
+ logd("Not starting refresh Timer as state is #{subscription.state}");
1858
+ return
1859
+ end
1860
+
1861
+ expires = response.expires.header_value
1862
+ if expires == nil
1863
+ logd("Not starting refresh Timer as expires is nil")
1864
+ return
1865
+ end
1866
+
1867
+ expiresDuration = (expires.to_i - 10) * 1000
1868
+
1869
+ if (expiresDuration < 0)
1870
+ expiresDuration = expires.to_i * 500
1871
+ end
1872
+
1873
+ logd("Starting refresh Timer for #{expiresDuration}");
1874
+ _schedule_timer_for_subscription(expiresDuration, subscription)
1875
+ end
1876
+
1877
+ # Persists for REGISTRAR not REGISTRANT
1878
+ def _create_registration_object(message, persist=true)
1879
+ if message[:to]
1880
+ aor = message.to.header_value
1881
+ else
1882
+ log_and_raise "Invalid registration", ArgumentError
1883
+ end
1884
+
1885
+ key = sprintf("|%s", aor)
1886
+ reg_list = SIP::Locator[:RegistrationStore].get(key) if persist
1887
+ unless reg_list
1888
+ logd("Creating new registration object.")
1889
+ reg_list =[]
1890
+ if message[:contact]
1891
+ message.contacts.each do|cn|
1892
+ registration = Registration.add_registration_data(cn,message)
1893
+ reg_list.push(registration) if registration.expires.to_i != 0
1894
+ end
1895
+ end
1896
+ else
1897
+ logd("Updating registration object.")
1898
+ message.contacts.each do|cn|
1899
+ updated = Registration.update_registration_data(cn, reg_list, message)
1900
+ if not updated
1901
+ registration = Registration.add_registration_data(cn,message)
1902
+ reg_list.push(registration) if registration.expires.to_i != 0
1903
+ end
1904
+ end
1905
+ end
1906
+
1907
+ if message[:expires]
1908
+ if message.expires.header_value == '0' and message.contact.header_value == '*'
1909
+ reg_list.clear
1910
+ end
1911
+ end
1912
+ SIP::Locator[:RegistrationStore].put(key, reg_list) if persist
1913
+ return reg_list
1914
+ end
1915
+
1916
+ def _schedule_timer_for_registration(duration, registration)
1917
+ logd("Scheduling a registration refresh timer for #{duration}")
1918
+ SIP::Locator[:Sth].schedule_for(self, registration, nil, :registration, duration)
1919
+ end
1920
+
1921
+ def start_registration_expiry_timer
1922
+ expires = '99999'
1923
+ @registrations.each do | reg_data |
1924
+ expires = reg_data.expires if expires.to_i > reg_data.expires.to_i
1925
+ end
1926
+ if expires.to_i == 0
1927
+ logd("Not starting registration refresh Timer as expires is 0")
1928
+ return
1929
+ end
1930
+
1931
+ expiresDuration = (expires.to_i - 10)*1000
1932
+
1933
+ if (expiresDuration < 0)
1934
+ expiresDuration = expires.to_i * 500
1935
+ end
1936
+ logd("Starting registration expiry Timer for #{expiresDuration}");
1937
+ _schedule_timer_for_registration(expiresDuration, @registrations)
1938
+ end
1939
+
1940
+ def offer_answer=(val)
1941
+ @offer_answer.close if @offer_answer && val.nil?
1942
+ @offer_answer = val
1943
+ end
1944
+
1945
+
1946
+ protected :_get_sq_lock, :_get_recorder, :_remove_recorder
1947
+ private :_on_request, :_on_response, :_on_common_sip, :_do_record_sip, :_check_cancel_state,
1948
+ :_check_for_pending_cancel, :_fixed_local_tag, :_send_common,
1949
+ :_schedule_timer_for_session, :_increment_local_cseq, :_update_and_check_dialog_state_on_request,
1950
+ :_add_route_headers_if_present, :_check_transport_and_destination, :_create_registration_object
1951
+
1952
+ end