Class: Freeswitch::ESL::Connection
- Inherits:
-
Object
- Object
- Freeswitch::ESL::Connection
- Defined in:
- lib/freeswitch/esl/connection.rb,
lib/freeswitch/esl/connection/message_reader.rb,
lib/freeswitch/esl/connection/event_dispatcher.rb
Overview
Base ESL connection that handles the wire protocol.
Subclasses are responsible for providing an open socket and calling #initialize_socket to start the reader and event-dispatcher threads.
Threading model:
* A *reader thread* reads messages from the socket and routes them:
- command/api replies → @response_queue (consumed by the calling thread)
- events → @event_queue (consumed by the dispatcher thread)
* A *dispatcher thread* processes @event_queue and calls registered handlers.
* Command execution is serialised by @send_mutex so that each send+receive
pair is atomic and responses are never mixed up.
* On timeout the connection is closed because the protocol state is unknown.
Direct Known Subclasses
Defined Under Namespace
Classes: EventDispatcher, MessageReader
Constant Summary collapse
- MSG_TERMINATOR =
"\n\n"- DEFAULT_TIMEOUT =
5
Instance Method Summary collapse
-
#api(command, args = nil, timeout: DEFAULT_TIMEOUT) ⇒ Object
Execute a synchronous
apicommand. -
#bgapi(command, args = nil, timeout: DEFAULT_TIMEOUT, &block) ⇒ Object
Execute a background
bgapicommand. - #close ⇒ Object
- #closed? ⇒ Boolean
-
#filter(header, value) ⇒ Object
Add an event-header filter so FreeSWITCH only sends matching events.
-
#initialize(socket) ⇒ Connection
constructor
A new instance of Connection.
-
#on(event_name) ⇒ Object
Register a handler block for an event name.
-
#send_command(command, timeout: DEFAULT_TIMEOUT) ⇒ Object
Send a raw ESL command and return the Message reply.
-
#subscribe(*event_names) ⇒ Object
Subscribe to one or more event names (JSON format).
-
#unsubscribe(*event_names) ⇒ Object
Cancel subscriptions.
Constructor Details
#initialize(socket) ⇒ Connection
Returns a new instance of Connection.
29 30 31 |
# File 'lib/freeswitch/esl/connection.rb', line 29 def initialize(socket) initialize_socket(socket) end |
Instance Method Details
#api(command, args = nil, timeout: DEFAULT_TIMEOUT) ⇒ Object
Execute a synchronous api command. Returns the Message with the result in Protocol::Message#body.
47 48 49 50 51 52 53 |
# File 'lib/freeswitch/esl/connection.rb', line 47 def api(command, args = nil, timeout: DEFAULT_TIMEOUT) parts = ["api", command, args].compact @send_mutex.synchronize do @socket.write("#{parts.join(' ')}#{MSG_TERMINATOR}") receive_response(timeout: timeout) end end |
#bgapi(command, args = nil, timeout: DEFAULT_TIMEOUT, &block) ⇒ Object
Execute a background bgapi command. Returns the Job-UUID string. The optional block is called with the BACKGROUND_JOB Event when the result arrives.
58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/freeswitch/esl/connection.rb', line 58 def bgapi(command, args = nil, timeout: DEFAULT_TIMEOUT, &block) parts = ["bgapi", command, args].compact job_uuid = @send_mutex.synchronize do @socket.write("#{parts.join(' ')}#{MSG_TERMINATOR}") reply = receive_response(timeout: timeout) reply["Job-UUID"] end @event_dispatcher.register_bgapi_handler(job_uuid, block) if block && job_uuid job_uuid end |
#close ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/freeswitch/esl/connection.rb', line 103 def close return if @closed @closed = true begin @socket.close rescue StandardError nil end @message_reader&.stop @event_dispatcher&.stop end |
#closed? ⇒ Boolean
99 100 101 |
# File 'lib/freeswitch/esl/connection.rb', line 99 def closed? @closed end |
#filter(header, value) ⇒ Object
Add an event-header filter so FreeSWITCH only sends matching events.
87 88 89 |
# File 'lib/freeswitch/esl/connection.rb', line 87 def filter(header, value) send_command("filter #{header} #{value}") end |
#on(event_name) ⇒ Object
Register a handler block for an event name. Use “ALL” to handle every event. Multiple handlers per event name are supported. Returns self for chaining.
94 95 96 97 |
# File 'lib/freeswitch/esl/connection.rb', line 94 def on(event_name, &) @event_dispatcher.on(event_name, &) self end |
#send_command(command, timeout: DEFAULT_TIMEOUT) ⇒ Object
Send a raw ESL command and return the Message reply.
34 35 36 37 38 39 40 41 42 43 |
# File 'lib/freeswitch/esl/connection.rb', line 34 def send_command(command, timeout: DEFAULT_TIMEOUT) raise DisconnectedError, "Connection is closed" if closed? # Keep write+read together so one thread cannot read another thread's # reply by mistake. @send_mutex.synchronize do @socket.write("#{command}#{MSG_TERMINATOR}") receive_response(timeout: timeout) end end |
#subscribe(*event_names) ⇒ Object
Subscribe to one or more event names (JSON format). Pass no arguments or “ALL” to receive every event.
72 73 74 75 |
# File 'lib/freeswitch/esl/connection.rb', line 72 def subscribe(*event_names) events = event_names.empty? ? "ALL" : event_names.join(" ") send_command("event json #{events}") end |
#unsubscribe(*event_names) ⇒ Object
Cancel subscriptions. Without arguments cancels all events.
78 79 80 81 82 83 84 |
# File 'lib/freeswitch/esl/connection.rb', line 78 def unsubscribe(*event_names) if event_names.empty? send_command("noevents") else event_names.each { |e| send_command("nixevent #{e}") } end end |