Class: Freeswitch::ESL::Connection

Inherits:
Object
  • Object
show all
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

Client

Defined Under Namespace

Classes: EventDispatcher, MessageReader

Constant Summary collapse

MSG_TERMINATOR =
"\n\n"
DEFAULT_TIMEOUT =
5

Instance Method Summary collapse

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

#closeObject



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

Returns:

  • (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.

Raises:



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