Class: Roby::Interface::REST::Server
- Defined in:
- lib/roby/interface/rest/server.rb
Overview
A thin-based server class that provides the REST API in-process
Defined Under Namespace
Classes: InvalidServer, RackMiddleware, Timeout
Constant Summary collapse
- VERBOSE_MIDDLEWARES =
[Rack::CommonLogger, Rack::ShowExceptions].freeze
Instance Attribute Summary collapse
-
#app ⇒ Object
readonly
The application that is being exposed by this server.
-
#host ⇒ String
readonly
The host the server is bound to.
-
#main_route ⇒ String
readonly
The path under which this API will be available, e.g.
Class Method Summary collapse
-
.attach_api_to_interface(api, interface, storage = {}, roby_execute: true) ⇒ Object
private
Helper method that transforms a Grape API class so that it gets an #interface accessor that provides the interface object the API is meant to work on.
-
.server_alive?(host, port, main_route: "/api") ⇒ Boolean
Tests whether the server is actually alive.
Instance Method Summary collapse
-
#create_thin_thread(server, sync) ⇒ Object
private
Create the underlying thread that starts the thin server.
-
#initialize(app, host: "0.0.0.0", port: Roby::Interface::DEFAULT_REST_PORT, api: REST::API, main_route: "/api", storage: {}, threads: 1, roby_execute: true, middlewares: VERBOSE_MIDDLEWARES, **thin_options) ⇒ Server
constructor
Create a new server.
-
#join(timeout: nil) ⇒ Object
Waits for the server to stop.
-
#port(timeout: 0) ⇒ Object
The server port.
-
#running? ⇒ Boolean
Whether the server is running.
- #server_alive? ⇒ Boolean
-
#start(wait_timeout: 5) ⇒ Object
Starts the server.
-
#stop(join_timeout: 10) ⇒ Object
Asks the server to stop.
-
#wait_start(timeout: 10) ⇒ Object
Wait for the server to be properly booted.
Constructor Details
#initialize(app, host: "0.0.0.0", port: Roby::Interface::DEFAULT_REST_PORT, api: REST::API, main_route: "/api", storage: {}, threads: 1, roby_execute: true, middlewares: VERBOSE_MIDDLEWARES, **thin_options) ⇒ Server
Create a new server
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 78 79 80 81 |
# File 'lib/roby/interface/rest/server.rb', line 41 def initialize(app, host: "0.0.0.0", port: Roby::Interface::DEFAULT_REST_PORT, api: REST::API, main_route: "/api", storage: {}, threads: 1, roby_execute: true, middlewares: VERBOSE_MIDDLEWARES, **) @app = app @host = host @main_route = main_route @interface = Interface.new(app) @wait_start = Concurrent::IVar.new api = self.class.attach_api_to_interface( api, @interface, storage, roby_execute: roby_execute ) rack_app = Rack::Builder.new do yield(self) if block_given? map main_route do middlewares.each { |m| use m } run api end end @server = Thin::Server.new( host, port, rack_app, signals: false, ** ) @server.threaded = (threads != 1) @server.threadpool_size = threads @server.silent = true if @server.backend.respond_to?(:port) @original_port = port @port = port if port != 0 else @original_port = @port = nil end end |
Instance Attribute Details
#app ⇒ Object (readonly)
The application that is being exposed by this server
16 17 18 |
# File 'lib/roby/interface/rest/server.rb', line 16 def app @app end |
#host ⇒ String (readonly)
The host the server is bound to
21 22 23 |
# File 'lib/roby/interface/rest/server.rb', line 21 def host @host end |
#main_route ⇒ String (readonly)
The path under which this API will be available, e.g. /api
26 27 28 |
# File 'lib/roby/interface/rest/server.rb', line 26 def main_route @main_route end |
Class Method Details
.attach_api_to_interface(api, interface, storage = {}, roby_execute: true) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Helper method that transforms a Grape API class so that it gets an #interface accessor that provides the interface object the API is meant to work on
116 117 118 119 120 121 122 |
# File 'lib/roby/interface/rest/server.rb', line 116 def self.attach_api_to_interface( api, interface, storage = {}, roby_execute: true ) RackMiddleware.new( api, interface, storage, roby_execute: roby_execute ) end |
.server_alive?(host, port, main_route: "/api") ⇒ Boolean
Tests whether the server is actually alive
It accesses the ‘ping’ endpoint and verifies its result
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/roby/interface/rest/server.rb', line 236 def self.server_alive?(host, port, main_route: "/api") test_value = rand(10) returned_value = RestClient.get( "http://#{host}:#{port}#{main_route}/ping", params: { value: test_value } ) if test_value != Integer(returned_value) raise InvalidServer, "unexpected server answer to 'ping', "\ "expected #{test_value} but got "\ "#{returned_value}" end true rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL false rescue RestClient::Exception => e raise InvalidServer, "unexpected server answer to 'ping': #{e}" end |
Instance Method Details
#create_thin_thread(server, sync) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Create the underlying thread that starts the thin server
144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/roby/interface/rest/server.rb', line 144 def create_thin_thread(server, sync) Thread.new do server.backend.start do if server.backend.respond_to?(:port) sync.set(server.backend.port) else sync.set(nil) end end end end |
#join(timeout: nil) ⇒ Object
Waits for the server to stop
208 209 210 211 212 213 214 215 216 217 |
# File 'lib/roby/interface/rest/server.rb', line 208 def join(timeout: nil) if timeout unless @server_thread.join(timeout) raise Timeout, "timed out while waiting for "\ "the server to stop" end else @server_thread.join end end |
#port(timeout: 0) ⇒ Object
The server port
If the port given to #initialize was zero, the method will return the actual port only if the server is fully started. Set timeout to a value greater than zero or nil to synchronize on it.
167 168 169 170 171 172 173 |
# File 'lib/roby/interface/rest/server.rb', line 167 def port(timeout: 0) return @port if @port return unless @original_port wait_start(timeout: timeout) @port = @wait_start.value! end |
#running? ⇒ Boolean
Whether the server is running
137 138 139 |
# File 'lib/roby/interface/rest/server.rb', line 137 def running? @server_thread&.alive? end |
#server_alive? ⇒ Boolean
224 225 226 227 228 |
# File 'lib/roby/interface/rest/server.rb', line 224 def server_alive? return false unless @wait_start.complete? self.class.server_alive?("localhost", port, main_route: main_route) end |
#start(wait_timeout: 5) ⇒ Object
Starts the server
131 132 133 134 |
# File 'lib/roby/interface/rest/server.rb', line 131 def start(wait_timeout: 5) @server_thread = create_thin_thread(@server, @wait_start) wait_start(timeout: wait_timeout) if wait_timeout != 0 end |
#stop(join_timeout: 10) ⇒ Object
Asks the server to stop
197 198 199 200 |
# File 'lib/roby/interface/rest/server.rb', line 197 def stop(join_timeout: 10) EventMachine.next_tick { @server.stop! } join(timeout: join_timeout) if join_timeout != 0 end |
#wait_start(timeout: 10) ⇒ Object
Wait for the server to be properly booted
184 185 186 187 188 189 |
# File 'lib/roby/interface/rest/server.rb', line 184 def wait_start(timeout: 10) @wait_start.wait(timeout) return if @wait_start.complete? raise Timeout, "timed out while waiting for the server to start" end |