sipfsm 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|