Class: Ruflet::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/ruflet/server.rb

Constant Summary collapse

WEBSOCKET_GUID =
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host: "0.0.0.0", port: 8550, &app_block) ⇒ Server

Returns a new instance of Server.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/ruflet/server.rb', line 17

def initialize(host: "0.0.0.0", port: 8550, &app_block)
  @host = host
  @port = port
  @app_block = app_block
  @sessions = {}
  @sessions_mutex = Mutex.new
  @connections = {}
  @connections_mutex = Mutex.new
  @running = false
  @server_socket = nil

  at_exit do
    begin
      stop
    rescue StandardError
      nil
    end
  end
end

Instance Attribute Details

#portObject (readonly)

Returns the value of attribute port.



13
14
15
# File 'lib/ruflet/server.rb', line 13

def port
  @port
end

Instance Method Details

#bind_server_socket!(max_attempts: 100) ⇒ Object

Raises:

  • (Errno::EADDRINUSE)


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/ruflet/server.rb', line 56

def bind_server_socket!(max_attempts: 100)
  requested = @port.to_i
  candidate = requested

  max_attempts.times do
    begin
      @server_socket = TCPServer.new(@host, candidate)
      @port = candidate
      if @port != requested && ENV["RUFLET_SUPPRESS_SERVER_BANNER"] != "1"
        warn "Requested port #{requested} is busy; bound to #{@port}"
      end
      return
    rescue Errno::EADDRINUSE
      candidate += 1
    end
  end

  raise Errno::EADDRINUSE, "Unable to bind starting at #{requested} after #{max_attempts} attempts"
end

#handle_upgraded_socket(io) ⇒ Object

For Rack-hosted mode: caller already performed the HTTP upgrade.



51
52
53
54
# File 'lib/ruflet/server.rb', line 51

def handle_upgraded_socket(io)
  ws = Ruflet::WebSocketConnection.new(io)
  run_connection(ws)
end

#reload_app!Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/ruflet/server.rb', line 99

def reload_app!
  snapshots = @sessions_mutex.synchronize { @sessions.to_a }

  snapshots.each do |session_key, current_page|
    ws = @connections_mutex.synchronize { @connections[session_key] }
    next unless ws

    refreshed_page = Page.new(
      session_id: current_page.session_id,
      client_details: current_page.client_details,
      sender: lambda do |action, payload|
        send_message(ws, action, payload)
      end
    )
    refreshed_page.title = "Ruflet App"

    @sessions_mutex.synchronize do
      @sessions[session_key] = refreshed_page
    end

    @app_block.call(refreshed_page)
    refreshed_page.update
  rescue StandardError => e
    warn "reload error: #{e.class}: #{e.message}"
  end
end

#startObject



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/ruflet/server.rb', line 37

def start
  previous_signals = trap_stop_signals
  bind_server_socket!
  @running = true
  print_server_banner
  accept_loop
rescue Interrupt
  nil
ensure
  stop
  restore_stop_signals(previous_signals)
end

#stopObject



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/ruflet/server.rb', line 76

def stop
  return unless @running || @server_socket

  @running = false

  server = @server_socket
  @server_socket = nil
  begin
    server&.close
  rescue IOError
    nil
  end

  live_connections = @connections_mutex.synchronize { @connections.values.dup }
  live_connections.each do |conn|
    begin
      conn.close
    rescue StandardError
      nil
    end
  end
end