Class: Barrister::Server
Overview
The Server class is responsible for taking an incoming request, validating the method and params, invoking the correct handler function (your code), and returning the result.
Server has a Barrister::Contract that is initialized in the contructor. It uses the Contract for validation.
The Server doesn’t do any network communication. It contains a default ‘handle_json` convenience method that encapsulates JSON serialization, and a lower level `handle` method. This will make it easy to add other serialization formats (such as MessagePack) later.
Instance Method Summary collapse
-
#add_handler(iface_name, handler) ⇒ Object
Register a handler class with the given interface name.
-
#handle(req) ⇒ Object
Handles a deserialized request and returns the result.
-
#handle_json(json_str) ⇒ Object
Handles a request encoded as JSON.
-
#handle_single(req) ⇒ Object
Internal method that validates and executes a single request.
-
#initialize(contract) ⇒ Server
constructor
Create a server with the given Barrister::Contract instance.
Methods included from Barrister
contract_from_file, #err_resp, #ok_resp, parse_method, rand_str
Constructor Details
#initialize(contract) ⇒ Server
Create a server with the given Barrister::Contract instance
144 145 146 147 |
# File 'lib/barrister.rb', line 144 def initialize(contract) @contract = contract @handlers = { } end |
Instance Method Details
#add_handler(iface_name, handler) ⇒ Object
Register a handler class with the given interface name
The ‘handler` is any Ruby class that contains methods for each function on the given IDL interface name.
These methods will be called when a request is handled by the Server.
155 156 157 158 159 160 161 |
# File 'lib/barrister.rb', line 155 def add_handler(iface_name, handler) iface = @contract.interface(iface_name) if !iface raise "No interface found with name: #{iface_name}" end @handlers[iface_name] = handler end |
#handle(req) ⇒ Object
Handles a deserialized request and returns the result
‘req` must either be a Hash (single request), or an Array (batch request)
‘handle` returns an Array of results for batch requests, and a single Hash for single requests.
183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/barrister.rb', line 183 def handle(req) if req.kind_of?(Array) resp_list = [ ] req.each do |r| resp_list << handle_single(r) end return resp_list else return handle_single(req) end end |
#handle_json(json_str) ⇒ Object
Handles a request encoded as JSON.
Returns the result as a JSON encoded string.
165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/barrister.rb', line 165 def handle_json(json_str) begin req = JSON::parse(json_str) resp = handle(req) rescue JSON::ParserError => e resp = err_resp({ }, -32700, "Unable to parse JSON: #{e.}") end # Note the `:ascii_only` usage here. Important. return JSON::generate(resp, { :ascii_only=>true }) end |
#handle_single(req) ⇒ Object
Internal method that validates and executes a single request.
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/barrister.rb', line 196 def handle_single(req) method = req["method"] if !method return err_resp(req, -32600, "No method provided on request") end # Special case - client is requesting the IDL bound to this server, so # we return it verbatim. No further validation is needed in this case. if method == "barrister-idl" return ok_resp(req, @contract.idl) end # Make sure we can find an interface and function on the IDL for this # request method string err_resp, iface, func = @contract.resolve_method(req) if err_resp != nil return err_resp end # Make sure that the params on the request match the IDL types err_resp = @contract.validate_params(req, func) if err_resp != nil return err_resp end params = [ ] if req["params"] params = req["params"] end # Make sure we have a handler bound to this Server for the interface. # If not, that means `server.add_handler` was not called for this interface # name. That's likely a misconfiguration. handler = @handlers[iface.name] if !handler return err_resp(req, -32000, "Server error. No handler is bound to interface #{iface.name}") end # Make sure that the handler has a method for the given function. if !handler.respond_to?(func.name) return err_resp(req, -32000, "Server error. Handler for #{iface.name} does not implement #{func.name}") end begin # Call the handler function. This is where your code gets invoked. result = handler.send(func.name, *params) # Verify that the handler function's return value matches the # correct type as specified in the IDL err_resp = @contract.validate_result(req, result, func) if err_resp != nil return err_resp else return ok_resp(req, result) end rescue RpcException => e # If the handler raised a RpcException, that's ok - return it unmodified. return err_resp(req, e.code, e., e.data) rescue => e # If any other error was raised, print it and return a generic error to the client puts e.inspect puts e.backtrace return err_resp(req, -32000, "Unknown error: #{e}") end end |