Class: Frpc::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/frpc/client.rb

Overview

Runs frpc as a child process and controls it over its admin API.

client = Frpc::Client.new("frpc.toml", user: "admin", password: "hunter2")
client.status
client.reload(File.read("frpc2.toml"))
client.stop

Or scoped, which always stops the child:

Frpc::Client.run("frpc.toml") { |c| pp c.status }

The config file must enable the admin server, otherwise there is nothing to talk to and #start raises:

webServer.addr = "127.0.0.1"
webServer.port = 7400

Defined Under Namespace

Classes: Error

Constant Summary collapse

DEFAULT_ADMIN =
{ host: "127.0.0.1", port: 7400 }.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_path, executable: nil, host: nil, port: nil, user: nil, password: nil, env: {}, log_to: nil, timeout: 10, start: true) ⇒ Client

host/port/user/password default to whatever webServer.* the config file declares, falling back to 127.0.0.1:7400. Pass them explicitly to skip that sniffing entirely.

Raises:



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/frpc/client.rb', line 42

def initialize(config_path,
               executable: nil,
               host: nil, port: nil, user: nil, password: nil,
               env: {}, log_to: nil, timeout: 10, start: true)
  @config_path = File.expand_path(config_path)
  raise Error, "no such frpc config file: #{@config_path}" unless File.exist?(@config_path)

  @executable = Frpc::Ruby.executable(exe_path: executable)
  @env = env
  @log_to = log_to
  @timeout = timeout

  settings = web_server_settings
  @admin = Admin.new(
    host: host || settings[:addr] || DEFAULT_ADMIN[:host],
    port: port || settings[:port] || DEFAULT_ADMIN[:port],
    user: user || settings[:user],
    password: password || settings[:password],
  )

  self.start if start
end

Instance Attribute Details

#adminObject (readonly)

Returns the value of attribute admin.



28
29
30
# File 'lib/frpc/client.rb', line 28

def admin
  @admin
end

#config_pathObject (readonly)

Returns the value of attribute config_path.



28
29
30
# File 'lib/frpc/client.rb', line 28

def config_path
  @config_path
end

#pidObject (readonly)

Returns the value of attribute pid.



28
29
30
# File 'lib/frpc/client.rb', line 28

def pid
  @pid
end

Class Method Details

.run(config_path, **kwargs) ⇒ Object



30
31
32
33
34
35
36
37
# File 'lib/frpc/client.rb', line 30

def self.run(config_path, **kwargs)
  client = new(config_path, **kwargs)
  begin
    yield client
  ensure
    client.stop
  end
end

Instance Method Details

#configObject



82
# File 'lib/frpc/client.rb', line 82

def config = admin.config

#proxy(name) ⇒ Object

Per-proxy hash for name, or nil when frpc does not know that proxy.



86
87
88
89
90
91
92
# File 'lib/frpc/client.rb', line 86

def proxy(name)
  status.to_h.each_value do |proxies|
    found = Array(proxies).find { |p| p["name"] == name.to_s }
    return found if found
  end
  nil
end

#reload(new_toml = nil) ⇒ Object



83
# File 'lib/frpc/client.rb', line 83

def reload(new_toml = nil) = admin.reload(new_toml)

#running?Boolean

Returns:

  • (Boolean)


73
74
75
76
77
78
79
# File 'lib/frpc/client.rb', line 73

def running?
  return false unless @pid

  Process.waitpid(@pid, Process::WNOHANG).nil?
rescue Errno::ECHILD, Errno::ESRCH
  false
end

#startObject

Raises:



65
66
67
68
69
70
71
# File 'lib/frpc/client.rb', line 65

def start
  raise Error, "frpc is already running (pid #{@pid})" if running?

  @pid = Process.spawn(@env, @executable, "-c", @config_path, **spawn_options)
  wait_for_admin
  self
end

#statusObject



81
# File 'lib/frpc/client.rb', line 81

def status = admin.status

#stop(timeout: @timeout) ⇒ Object

Asks frpc to shut down gracefully, then reaps the child. Falls back to TERM and finally KILL if it does not exit within timeout.



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/frpc/client.rb', line 96

def stop(timeout: @timeout)
  return unless @pid

  begin
    admin.stop
  rescue Admin::Error
    # Admin server already gone or never came up — signals below still apply.
  end

  reap(timeout: timeout) || terminate(timeout: timeout)
  @pid = nil
end