Class: StaticSiteBuilder::WebSocketServer

Inherits:
Object
  • Object
show all
Defined in:
lib/static_site_builder/websocket_server.rb

Overview

WebSocket server for live reload functionality during development.

Watches for rebuild notifications via a reload file and broadcasts reload messages to connected browser clients, enabling automatic page refresh.

Constant Summary collapse

ACCEPT_RETRY_INTERVAL =

Sleep intervals for thread operations (in seconds)

0.1
WATCH_POLL_INTERVAL =

Retry delay when accepting connections fails

0.3
CLIENT_KEEPALIVE_INTERVAL =

How often to check for rebuild notifications

1

Instance Method Summary collapse

Constructor Details

#initialize(port: StaticSiteBuilder::DEFAULT_WS_PORT, reload_file: nil) ⇒ WebSocketServer

Initializes a new WebSocket server instance.

Parameters:

  • port (Integer) (defaults to: StaticSiteBuilder::DEFAULT_WS_PORT) —

    Port number for the WebSocket server (default: 3001)

  • reload_file (Pathname, nil) (defaults to: nil) —

    Path to the reload notification file. If nil, defaults to .reload in current directory.



23
24
25
26
27
28
# File 'lib/static_site_builder/websocket_server.rb', line 23

def initialize(port: StaticSiteBuilder::DEFAULT_WS_PORT, reload_file: nil)
  @port = port
  @reload_file = reload_file || Pathname.new(Dir.pwd).join(".reload")
  @clients = []
  @running = false
end

Instance Method Details

#start ⇒ void

This method returns an undefined value.

Starts the WebSocket server and begins watching for rebuild notifications.

Spawns two background threads: one for accepting client connections and one for watching the reload file for changes. Returns immediately after starting the threads.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/static_site_builder/websocket_server.rb', line 37

def start
  @running = true
  @server = TCPServer.new("127.0.0.1", @port)

  # Initialize reload file if it doesn't exist
  File.write(@reload_file, Time.current.to_f.to_s) unless @reload_file.exist?
  @last_mtime = @reload_file.mtime

  # Accept client connections in background thread
  @accept_thread = Thread.new do
    while @running
      begin
        client = @server.accept
        Thread.new { handle_client(client) }
      rescue IOError, Errno::EBADF, Errno::ECONNABORTED
        # Connection errors are expected during shutdown or network issues
        # Retry accepting connections unless server is stopping
        sleep ACCEPT_RETRY_INTERVAL
        break unless @running
      end
    end
  end

  # Watch for rebuild notifications in background thread
  @watch_thread = Thread.new do
    while @running
      begin
        sleep WATCH_POLL_INTERVAL
        if @reload_file.exist? && @reload_file.mtime > @last_mtime
          @last_mtime = @reload_file.mtime
          broadcast("reload")
        end
      rescue Errno::ENOENT, Errno::EACCES, SystemCallError
        # File system errors during watch are non-fatal
        # Continue watching unless server is stopping
        sleep WATCH_POLL_INTERVAL
        break unless @running
      end
    end
  end
end

#stop ⇒ void

This method returns an undefined value.

Stops the WebSocket server gracefully.

Closes all client connections, stops accepting new connections, stops watching for rebuilds, and closes the server socket.



85
86
87
88
89
90
91
# File 'lib/static_site_builder/websocket_server.rb', line 85

def stop
  @running = false
  @clients.each { |client| safe_close(client) }
  safe_close(@server) if @server
  safe_kill_thread(@accept_thread)
  safe_kill_thread(@watch_thread)
end