sipfsm 0.1.2
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.
- checksums.yaml +15 -0
- data/lib/sipfsm.rb +306 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZmNhMzE5N2M0NTc1YWE1NzMwY2YzYTcwOGIyMTU4YjViOTg2ZWQ1ZA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTkxYzE1NmJkN2JjNTJlZTNiMjRkZmMxZTk3NGQ2MWJiNDhjZmJiMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MWZjNmQ3MzA3ZDVmNjE5MjM1ODU2MjU3OTM0M2UxMmM4NmMxYmNmMjJlNzZh
|
10
|
+
NGExYjQ3YzlhNTg3MDFiNWQ4MmVkNzE5ZWY4MTZjMzcxYWM5ZGMzYTBhNjNh
|
11
|
+
MzU5MDljYWQ2NGViMzg4ZGExYTBmNTBkYzliNmVjZTMxOTQ1NjU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjNiMDhkMTU0NTVhZTRjZjdiZDAxZjdmODgxOGM0ZTg0ZTcyMDI0Yjk2NDE3
|
14
|
+
Y2YzMDgwNTliMzk1NmVhOWZlZDA1NDU4YmM3ZjI2ZDY5Y2IzZjRkOTgzOGM4
|
15
|
+
ZGE2NTQzMGQyZTdiZGU3MWJhMGRhYTk3NTg5MDYzMTc1ZDUzZGI=
|
data/lib/sipfsm.rb
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
require 'java'
|
2
|
+
require 'simplefsm'
|
3
|
+
|
4
|
+
# SipFSM
|
5
|
+
# SIP application development using SimpleFSM DSL
|
6
|
+
|
7
|
+
module SipFSM
|
8
|
+
class SipFSM < Java::javax.servlet.sip.SipServlet
|
9
|
+
include javax.servlet.sip.TimerListener
|
10
|
+
include javax.servlet.sip.SipSessionListener
|
11
|
+
|
12
|
+
include SimpleFSM
|
13
|
+
FSM_STATE_ATTR = 'sipFSM_STATE'
|
14
|
+
|
15
|
+
# Method service is overriden in order to get servlet context.
|
16
|
+
# Then the service method of the Java base class is called.
|
17
|
+
def service(req, res)
|
18
|
+
msg = req || res
|
19
|
+
$servlet_context = msg.session.servlet_context if !$servlet_context
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
# Standard SIP servlet request dispatching
|
24
|
+
# is overriden and modified to call the DSL event methods.
|
25
|
+
def doRequest(request)
|
26
|
+
m = request.get_method
|
27
|
+
fsmm = "sip#{m}".to_sym
|
28
|
+
run if !fsm_prepare_state([request, nil])
|
29
|
+
|
30
|
+
if fsm_state_responds_to? @state, fsmm
|
31
|
+
send(fsmm, request, nil)
|
32
|
+
elsif fsm_state_responds_to? @state, :sipREQUEST_ANY
|
33
|
+
send(:sipREQUEST_ANY, request, nil)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Standard SIP servlet response dispatching
|
40
|
+
# is overriden and modified to call the DSL event methods.
|
41
|
+
def doResponse(response)
|
42
|
+
m = response.get_status.to_s
|
43
|
+
run if ! fsm_prepare_state([nil, response])
|
44
|
+
|
45
|
+
resp_exact = "sipRESPONSE_#{m}".to_sym
|
46
|
+
resp_group = "sipRESPONSE_#{m[/./].to_s}xx".to_sym
|
47
|
+
|
48
|
+
if fsm_state_responds_to? @state, resp_exact
|
49
|
+
send(resp_exact, nil, response)
|
50
|
+
|
51
|
+
elsif fsm_state_responds_to? @state, resp_group
|
52
|
+
send(resp_group, nil, response)
|
53
|
+
|
54
|
+
elsif fsm_state_responds_to? @state, :sipRESPONSE_ANY
|
55
|
+
send(:sipRESPONSE_ANY, nil, response)
|
56
|
+
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# creates and returns INVITE request
|
63
|
+
def self.create_request(app_session, method, from, to)
|
64
|
+
sip_factory = $servlet_context.get_attribute('javax.servlet.sip.SipFactory')
|
65
|
+
addr_from = sip_factory.create_address(sip_factory.create_uri(from[:uri]), from[:display_name])
|
66
|
+
addr_to = sip_factory.create_address(sip_factory.create_uri(to[:uri]), to[:display_name])
|
67
|
+
|
68
|
+
req = sip_factory.create_request(app_session, method, addr_from, addr_to);
|
69
|
+
req
|
70
|
+
end
|
71
|
+
|
72
|
+
# returns application session
|
73
|
+
def self.get_application_session_by_id(app_session_id)
|
74
|
+
return nil if !app_session_id
|
75
|
+
util = $servlet_context.get_attribute('javax.servlet.sip.SipSessionsUtil')
|
76
|
+
util.get_application_session_by_id(app_session_id)
|
77
|
+
end
|
78
|
+
|
79
|
+
# returns sip application session or creates one if flag is true
|
80
|
+
def self.http_get_application_session(http_request, create_flag, key_sufix="")
|
81
|
+
# HttpSession <=> ConvergedHttpSession
|
82
|
+
sid = http_request.env['java.servlet_request'].get_session().get_id
|
83
|
+
|
84
|
+
util = $servlet_context.get_attribute('javax.servlet.sip.SipSessionsUtil')
|
85
|
+
app_key = "sipfsmApp_#{key_sufix}#{sid}"
|
86
|
+
app = util.get_application_session_by_key(app_key, create_flag)
|
87
|
+
app
|
88
|
+
end
|
89
|
+
|
90
|
+
# returns sip-fsm state saved in application session attribute
|
91
|
+
def self.get_fsm_state_by_app_id(app_session_id)
|
92
|
+
return nil if !app_session_id
|
93
|
+
app_session = get_application_session_by_id(app_session_id)
|
94
|
+
if app_session
|
95
|
+
app_session.get_attribute(FSM_STATE_ATTR)
|
96
|
+
else
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# returns all SIP sessions bound to the given application session
|
102
|
+
def self.get_sip_sessions_by_app_id(app_session_id)
|
103
|
+
return nil if !app_session_id
|
104
|
+
app_session = get_application_session_by_id(app_session_id)
|
105
|
+
app_session.get_sessions("SIP")
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.get_attr_const
|
109
|
+
FSM_STATE_ATTR
|
110
|
+
end
|
111
|
+
|
112
|
+
def prepare_state_by_req msg
|
113
|
+
fsm_prepare_state [msg, nil]
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
##### Overriden methods (for FSM) ###########################
|
118
|
+
|
119
|
+
# Loading and saving application FSM state from application attribute
|
120
|
+
def fsm_prepare_state msgs
|
121
|
+
m = msgs[0] || msgs[1]
|
122
|
+
|
123
|
+
@sip_session = m.get_session
|
124
|
+
@app_session = m.get_application_session
|
125
|
+
s = @app_session.get_attribute(FSM_STATE_ATTR)
|
126
|
+
@state = s if s
|
127
|
+
s
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def fsm_save_state request
|
132
|
+
@app_session.set_attribute(FSM_STATE_ATTR, @state)
|
133
|
+
@state
|
134
|
+
end
|
135
|
+
|
136
|
+
####### Helper methods ###############
|
137
|
+
|
138
|
+
# Dynamic methods:
|
139
|
+
# send_response_YYY - for sending response with
|
140
|
+
# the code specified in the method name (send_response_200 etc.)
|
141
|
+
# is_XXX? - check if the SIP request is of type (method) specified
|
142
|
+
# in the method name (is_INVITE? etc.)
|
143
|
+
#
|
144
|
+
def method_missing(name, args)
|
145
|
+
if name.to_s =~ /send_response_(.*)/
|
146
|
+
args[0].create_response($1.to_i).send
|
147
|
+
elsif name.to_s =~ /is_(.*)_request?/
|
148
|
+
args[0].get_method == $1
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# proxy request to given URI
|
153
|
+
def proxy_to_helper(request, touri, recroute=false)
|
154
|
+
l_proxy = request.get_proxy
|
155
|
+
l_proxy.set_record_route(recroute)
|
156
|
+
|
157
|
+
to_URI = create_uri touri
|
158
|
+
l_proxy.proxy_to(to_URI)
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
# creates URI java object using SipFactory
|
163
|
+
def create_uri(str_uri)
|
164
|
+
if @sip_session
|
165
|
+
sipURI = @sip_session.get_servlet_context.get_attribute('javax.servlet.sip.SipFactory').create_uri('sip:' + str_uri)
|
166
|
+
sipURI
|
167
|
+
else
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
# copies content (SDP) from message m1 to message m2
|
174
|
+
def copy_msg_content(m1, m2)
|
175
|
+
if m1.get_content_length > 0
|
176
|
+
m2.set_content(m1.get_raw_content, m1.get_content_type)
|
177
|
+
|
178
|
+
enc = m1.get_character_encoding
|
179
|
+
m2.set_character_encoding(enc) if enc and enc.length > 0
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Sends SIP request and sets custom session attributes
|
184
|
+
# given in a hash as a second element in attributes
|
185
|
+
def send_req args
|
186
|
+
req = args[0]
|
187
|
+
if args.size > 2
|
188
|
+
attrib = args[2]
|
189
|
+
end
|
190
|
+
s = req.get_session
|
191
|
+
s.set_handler(self.class.to_s)
|
192
|
+
if attrib and attrib.size > 0 and attrib.class = Hash
|
193
|
+
attrib.each do |k, v|
|
194
|
+
s.set_attribute(k.to_s, v)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
req.send
|
198
|
+
end
|
199
|
+
|
200
|
+
###########################################################
|
201
|
+
# Methods that can be called directly from FSM definition.
|
202
|
+
# All methods have atributes according to the SipFSM standard
|
203
|
+
# meaning the first in the argument array is request and
|
204
|
+
# the second is response.
|
205
|
+
|
206
|
+
def invalidate_session msgs
|
207
|
+
msg = msgs[0] || msgs[1]
|
208
|
+
msg.get_session.invalidate
|
209
|
+
# ACK is sent by the SIP servlets container
|
210
|
+
end
|
211
|
+
|
212
|
+
def send_OK msgs
|
213
|
+
req = msgs[0]
|
214
|
+
req.create_response(200).send
|
215
|
+
end
|
216
|
+
|
217
|
+
def send_ACK msgs
|
218
|
+
res = msgs[1]
|
219
|
+
res.create_ack.send
|
220
|
+
end
|
221
|
+
|
222
|
+
def send_BYE msgs
|
223
|
+
req, res = msgs
|
224
|
+
m = req || res
|
225
|
+
m.get_session.create_request("BYE").send
|
226
|
+
end
|
227
|
+
|
228
|
+
def cancel_req args
|
229
|
+
req = args[0]
|
230
|
+
req.create_cancel.send
|
231
|
+
req.session.invalidate
|
232
|
+
end
|
233
|
+
|
234
|
+
# B2B Helper methods ##################################
|
235
|
+
|
236
|
+
# forwards request to the linked session (B2BUA Helper)
|
237
|
+
def b2b_forward_message msgs
|
238
|
+
req, res = msgs
|
239
|
+
r = req || res.get_request
|
240
|
+
begin
|
241
|
+
b2b = r.get_b2bua_helper
|
242
|
+
linked = b2b.get_linked_session(r.get_session)
|
243
|
+
if linked
|
244
|
+
other_leg = nil
|
245
|
+
resp_session = res.get_session
|
246
|
+
resp_request = res.get_request
|
247
|
+
if resp_request.is_initial
|
248
|
+
other_leg = b2b.create_response_to_original_request(linked, res.get_status, res.get_reason_phrase)
|
249
|
+
else
|
250
|
+
other_req = b2b.get_linked_sip_servlet_request(resp_request)
|
251
|
+
other_leg = other_req.create_response(res.get_status, res.get_reason_phrase)
|
252
|
+
end
|
253
|
+
copy_msg_content(res, other_leg)
|
254
|
+
other_leg.send
|
255
|
+
else
|
256
|
+
raise "No linked session."
|
257
|
+
end
|
258
|
+
rescue Exception => e
|
259
|
+
puts "Error: #{e.message}"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# send BYE to the linked session
|
264
|
+
def b2bua_BYE_other msgs
|
265
|
+
req, res = msgs
|
266
|
+
req ||= res.get_request
|
267
|
+
current_sess = req.get_session
|
268
|
+
b2b = req.get_b2bua_helper
|
269
|
+
session2 =
|
270
|
+
b2b.get_linked_session(current_sess)
|
271
|
+
session2.create_request("BYE").send
|
272
|
+
end
|
273
|
+
|
274
|
+
# send BYE to both linked session
|
275
|
+
def b2bua_BYE_both msgs
|
276
|
+
req, res = msgs
|
277
|
+
req ||= res.request
|
278
|
+
session1 = req.session
|
279
|
+
session2 = req.b2bua_helper.get_linked_session(session1)
|
280
|
+
session1.create_request("BYE").send
|
281
|
+
session2.create_request("BYE").send
|
282
|
+
session1.invalidate
|
283
|
+
session2.invalidate
|
284
|
+
end
|
285
|
+
|
286
|
+
# Send initial request using B2BUA helper.
|
287
|
+
# The third and other argument array elements
|
288
|
+
# can be attributes to save into the SIP session.
|
289
|
+
def b2b_send_initial_req args
|
290
|
+
req = args[0]
|
291
|
+
if args.size > 2
|
292
|
+
attrib = args[2]
|
293
|
+
end
|
294
|
+
new_req = req.b2bua_helper.create_request(req)
|
295
|
+
s = new_req.session
|
296
|
+
s.set_attribute('initialINVITE', new_req)
|
297
|
+
if attrib and attrib.size > 0 and attrib.is_a?(Hash)
|
298
|
+
attrib.each do |k, v|
|
299
|
+
s.set_attribute(k.to_s, v)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
new_req.send
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sipfsm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Edin Pjanic
|
8
|
+
- Amer Hasanovic
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: simplefsm
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0.1'
|
21
|
+
requirement: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0.1'
|
26
|
+
prerelease: false
|
27
|
+
type: :runtime
|
28
|
+
description: SIP application development in Ruby using SimpleFSM, a simple and lightweight domain specific language (DSL).
|
29
|
+
email:
|
30
|
+
- edin@ictlab.com.ba
|
31
|
+
- amer@ictlab.com.ba
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- lib/sipfsm.rb
|
37
|
+
homepage: http://github.com/edictlab/SipFSM
|
38
|
+
licenses: []
|
39
|
+
metadata: {}
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 2.0.3
|
57
|
+
signing_key:
|
58
|
+
specification_version: 4
|
59
|
+
summary: SipFSM - SIP application development in Ruby
|
60
|
+
test_files: []
|