Class: CelluloidPubsub::WebServer

Inherits:
Reel::Server::HTTP
  • Object
show all
Includes:
BaseActor
Defined in:
lib/celluloid_pubsub/web_server.rb

Overview

webserver to which socket connects should connect to . the server will dispatch each request into a new Reactor which will handle the action based on the message

Constant Summary collapse

HOST =

The hostname on which the webserver runs on by default

'0.0.0.0'
PATH =

The request path that the webserver accepts by default

'/ws'
CLASSIC_ADAPTER =

The name of the default adapter

'classic'

Instance Attribute Summary collapse

Attributes included from BaseActor

#config

Class Method Summary collapse

Instance Method Summary collapse

Methods included from BaseActor

celluloid_logger_class, celluloid_version, config, included, setup_actor_supervision, version_less_than_seventeen?

Methods included from Helper

action_subscribe?, fetch_gem_version, filtered_error?, find_loaded_gem, find_loaded_gem_property, get_parsed_version, log_debug, parse_options, setup_celluloid_exception_handler, setup_celluloid_logger, setup_log_file, #succesfull_subscription?, verify_gem_version

Constructor Details

#initialize(options = {}) ⇒ void

receives a list of options that are used to configure the webserver

:nocov:

Parameters:

  • options (Hash) (defaults to: {})

    the options that can be used to connect to webser and send additional data

Options Hash (options):

  • :hostname (String)

    The hostname on which the webserver runs on

  • :port (Integer)

    The port on which the webserver runs on

  • :path (String)

    The request path that the webserver accepts

  • :spy (Boolean)

    Enable this only if you want to enable debugging for the webserver


45
46
47
48
49
50
51
52
53
# File 'lib/celluloid_pubsub/web_server.rb', line 45

def initialize(options = {})
  Celluloid.boot unless Celluloid.running?
  @server_options = parse_options(options)
  @subscribers = {}
  @mutex = Mutex.new
  setup_celluloid_logger
  debug "CelluloidPubsub::WebServer example starting on #{hostname}:#{port}"
  super(hostname, port, { spy: spy, backlog: backlog }, &method(:on_connection))
end

Instance Attribute Details

#mutexMutex, Object

Returns:

  • (Mutex)

    The mutex that will synchronize actions on subscribers

  • (Object)

    the current value of mutex


20
21
22
# File 'lib/celluloid_pubsub/web_server.rb', line 20

def mutex
  @mutex
end

#server_optionsHash, Object

Returns:

  • (Hash)

    options used to configure the webserver

  • (Object)

    the current value of server_options


20
21
22
# File 'lib/celluloid_pubsub/web_server.rb', line 20

def server_options
  @server_options
end

#subscribersHash, Object

Returns:

  • (Hash)

    The hostname on which the webserver runs on

  • (Object)

    the current value of subscribers


20
21
22
# File 'lib/celluloid_pubsub/web_server.rb', line 20

def subscribers
  @subscribers
end

Class Method Details

.find_unused_portInteger

the method get from the socket connection that is already opened the port used.

Returns:

  • (Integer)

    returns the port that can be used to issue new connection

See Also:

  • #open_socket_on_unused_port

94
95
96
97
98
99
100
101
# File 'lib/celluloid_pubsub/web_server.rb', line 94

def self.find_unused_port
  @@unused_port ||= begin
    socket = open_socket_on_unused_port
    port = socket.addr[1]
    socket.close
    port
  end
end

.open_socket_on_unused_portTCPServer

the method will return the socket conection opened on the unused port

Returns:

  • (TCPServer)

    return the socket connection opened on a random port


61
62
63
64
65
# File 'lib/celluloid_pubsub/web_server.rb', line 61

def self.open_socket_on_unused_port
  return ::TCPServer.open('0.0.0.0', 0) if socket_families.key?('AF_INET')
  return ::TCPServer.open('::', 0) if socket_families.key?('AF_INET6')
  ::TCPServer.open(0)
end

.socket_familiesHash

the method will return the socket families avaiable

rubocop:disable ClassVars

Returns:

  • (Hash)

    return the socket families available as keys in the hash


84
85
86
# File 'lib/celluloid_pubsub/web_server.rb', line 84

def self.socket_families
  @@socket_families ||= Hash[*socket_infos.map { |af, *_| af }.uniq.zip([]).flatten]
end

.socket_infosArray

the method will return the socket information available as an array

Returns:

  • (Array)

    return the socket information available as an array


73
74
75
# File 'lib/celluloid_pubsub/web_server.rb', line 73

def self.socket_infos
  ::Socket::getaddrinfo('localhost', nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE)
end

Instance Method Details

#adapterBoolean

the method will return true if redis can be used otherwise false

Returns:

  • (Boolean)

    return true if redis can be used otherwise false


119
120
121
122
# File 'lib/celluloid_pubsub/web_server.rb', line 119

def adapter
  @adapter ||= @server_options.fetch('adapter', CelluloidPubsub::WebServer::CLASSIC_ADAPTER)
  @adapter.present? ? @adapter : CelluloidPubsub::WebServer::CLASSIC_ADAPTER
end

#backlogInteger

the method will return the number of connections allowed to the server

Returns:

  • (Integer)

    returns the number of connections allowed to the server


202
203
204
# File 'lib/celluloid_pubsub/web_server.rb', line 202

def backlog
  @backlog = @server_options.fetch('backlog', 1024)
end

#debug_enabled?Boolean

the method will return true if debug is enabled otherwise false

Returns:

  • (Boolean)

    returns true if debug is enabled otherwise false


130
131
132
133
# File 'lib/celluloid_pubsub/web_server.rb', line 130

def debug_enabled?
  @debug_enabled = @server_options.fetch('enable_debug', true)
  @debug_enabled == true
end

#dispatch_websocket_request(request) ⇒ void

This method returns an undefined value.

method will instantiate a new reactor object, will link the reactor to the current actor and will dispatch the request to the reactor

Parameters:

  • request (Reel::WebSocket)

    The request that was made to the webserver

See Also:


258
259
260
261
262
# File 'lib/celluloid_pubsub/web_server.rb', line 258

def dispatch_websocket_request(request)
  reactor = reactor_class.new
  Actor.current.link reactor
  route_websocket(reactor, request.websocket)
end

#handle_dispatched_message(reactor, data) ⇒ void

This method returns an undefined value.

If the message can be parsed into a Hash it will respond to the reactor's websocket connection with the same message in JSON format otherwise will try send the message how it is and escaped into JSON format

Parameters:

  • reactor (CelluloidPubsub::Reactor)

    The reactor that received an unhandled message

  • data (Object)

    The message that the reactor could not handle


306
307
308
309
310
311
# File 'lib/celluloid_pubsub/web_server.rb', line 306

def handle_dispatched_message(reactor, data)
  log_debug "#{self.class} trying to dispatch message  #{data.inspect}"
  message = reactor.parse_json_data(data)
  final_data = message.present? && message.is_a?(Hash) ? message.to_json : data.to_json
  reactor.websocket << final_data
end

#hostnameString

the method will return the hostname on which the server is running on

Returns:

  • (String)

    returns the hostname on which the server is running on


162
163
164
# File 'lib/celluloid_pubsub/web_server.rb', line 162

def hostname
  @hostname = @server_options.fetch('hostname', CelluloidPubsub::WebServer::HOST)
end

#log_file_pathString

the method will return the file path of the log file where debug messages will be printed

Returns:

  • (String)

    returns the file path of the log file where debug messages will be printed


152
153
154
# File 'lib/celluloid_pubsub/web_server.rb', line 152

def log_file_path
  @log_file_path = @server_options.fetch('log_file_path', nil)
end

#on_connection(connection) ⇒ void

This method returns an undefined value.

callback that will execute when receiving new conections If the connections is a websocket will call method #route_websocket and if the connection is HTTP will call method #route_request For websocket connections , the connection is detached from the server and dispatched to another actor

Parameters:

  • connection (Reel::WebSocket)

    The connection that was made to the webserver

See Also:


219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/celluloid_pubsub/web_server.rb', line 219

def on_connection(connection)
  while request = connection.request
    if request.websocket?
      log_debug "#{self.class} Received a WebSocket connection"

      # We're going to hand off this connection to another actor (Writer/Reader)
      # However, initially Reel::Connections are "attached" to the
      # Reel::Server::HTTP actor, meaning that the server manages the connection
      # lifecycle (e.g. error handling) for us.
      #
      # If we want to hand this connection off to another actor, we first
      # need to detach it from the Reel::Server (in this case, Reel::Server::HTTP)
      connection.detach
      dispatch_websocket_request(request)
      return
    else
      route_request connection, request
    end
  end
end

#pathString

the method will return the URL path on which will acceept connections

Returns:

  • (String)

    returns the URL path on which will acceept connections


182
183
184
# File 'lib/celluloid_pubsub/web_server.rb', line 182

def path
  @path = @server_options.fetch('path', CelluloidPubsub::WebServer::PATH)
end

#portString

the method will return the port on which will accept connections

Returns:

  • (String)

    returns the port on which will accept connections


172
173
174
# File 'lib/celluloid_pubsub/web_server.rb', line 172

def port
  @port ||= @server_options.fetch('port', nil) || self.class.find_unused_port
end

#reactor_classClass

returns the reactor class that will handle the connection depending if redis is enabled or not

Returns:

  • (Class)

    returns the reactor class that will handle the connection depending if redis is enabled or not

See Also:

  • #redis_enabled?

246
247
248
# File 'lib/celluloid_pubsub/web_server.rb', line 246

def reactor_class
  adapter == CelluloidPubsub::WebServer::CLASSIC_ADAPTER ? CelluloidPubsub::Reactor : "CelluloidPubsub::#{adapter.camelize}Reactor".constantize
end

#route_request(connection, request) ⇒ void

This method returns an undefined value.

HTTP connections are not accepted so this method will show 404 message “Not Found”

Parameters:

  • connection (Reel::WebSocket)

    The HTTP connection that was received

  • request (Reel::Request)

    The request that was made to the webserver and contains the type , the url, and the parameters


272
273
274
275
# File 'lib/celluloid_pubsub/web_server.rb', line 272

def route_request(connection, request)
  log_debug "404 Not Found: #{request.path}"
  connection.respond :not_found, 'Not found'
end

#route_websocket(reactor, socket) ⇒ void

This method returns an undefined value.

If the socket url matches with the one accepted by the server, it will dispatch the socket connection to a new reactor Reactor#work The new actor is linked to the webserver


287
288
289
290
291
292
293
294
295
# File 'lib/celluloid_pubsub/web_server.rb', line 287

def route_websocket(reactor, socket)
  url = socket.url
  if url == path || url == '/?'
    reactor.async.work(socket, Actor.current)
  else
    log_debug "Received invalid WebSocket request for: #{url}"
    socket.close
  end
end

#runObject

this method is overriden from the Reel::Server::HTTP in order to set the spy to the celluloid logger before the connection is accepted.

See Also:

  • #handle_connection

108
109
110
111
# File 'lib/celluloid_pubsub/web_server.rb', line 108

def run
  @spy = Celluloid.logger if spy
  loop { async.handle_connection @server.accept }
end

#shutdownvoid

This method returns an undefined value.

the method will terminate the current actor


141
142
143
144
# File 'lib/celluloid_pubsub/web_server.rb', line 141

def shutdown
  debug "#{self.class} tries to 'shudown'"
  terminate
end

#spyBoolean

the method will return true if connection to the server should be spied upon

Returns:

  • (Boolean)

    returns true if connection to the server should be spied upon, otherwise false


192
193
194
# File 'lib/celluloid_pubsub/web_server.rb', line 192

def spy
  @spy = @server_options.fetch('spy', false)
end