Class: MetaRPC::Interpreter

Inherits:
Object
  • Object
show all
Defined in:
lib/metarpc/interpreter.rb

Overview

Used to interpret RPC requests and execute them securely

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw_request) ⇒ Interpreter

Returns a new instance of Interpreter.



6
7
8
9
10
11
12
13
14
15
16
# File 'lib/metarpc/interpreter.rb', line 6

def initialize(raw_request)
  raw_input = JSON.parse(raw_request)

  @input = if raw_input.is_a?(Array)
             raw_input.map(&method(:validate_json_rpc_call))
           else
             validate_json_rpc_call(raw_input)
           end
rescue JSON::ParserError
  raise_error(:parse_error)
end

Instance Attribute Details

#inputObject (readonly)

Returns the value of attribute input.



4
5
6
# File 'lib/metarpc/interpreter.rb', line 4

def input
  @input
end

Instance Method Details

#execute(executor) ⇒ Object



18
19
20
21
22
# File 'lib/metarpc/interpreter.rb', line 18

def execute(executor)
  return input.map { |inp| execute_method(executor, inp) } if input.is_a?(Array)

  execute_method(executor, input)
end

#execute_method(executor, inp) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/metarpc/interpreter.rb', line 24

def execute_method(executor, inp)
  raise_error(:method_not_found) unless executor.class.json_rpc_methods.key?(inp[:method])

  result = if inp[:params].is_a?(Array)
             executor.public_send(inp[:method], *inp[:params])
           elsif inp[:params].is_a?(Hash)
             executor.public_send(inp[:method], inp[:params])
           elsif inp[:params].nil?
             executor.public_send(inp[:method])
           else
             raise_error(:internal_error)
           end

  {
    jsonrpc: '2.0',
    result: result,
    id: inp[:id]
  }
rescue RPCError => err
  {
    jsonrpc: '2.0',
    error: err.to_h,
    id: inp[:id]
  }
end

#raise_error(code) ⇒ Object

Raises:



92
93
94
# File 'lib/metarpc/interpreter.rb', line 92

def raise_error(code)
  raise RPCError.new(code), "Error on interpreter : #{code}"
end

#valid_id(id) ⇒ Object



76
77
78
79
80
# File 'lib/metarpc/interpreter.rb', line 76

def valid_id(id)
  return true if id.nil?

  id.is_a?(String) || id.nil? || id.is_a?(Integer) || id.is_a?(Float)
end

#valid_method_name(method) ⇒ Object



88
89
90
# File 'lib/metarpc/interpreter.rb', line 88

def valid_method_name(method)
  method.is_a?(String) && !method.start_with?('rpc.')
end

#valid_params(params) ⇒ Object



82
83
84
85
86
# File 'lib/metarpc/interpreter.rb', line 82

def valid_params(params)
  return true if params.nil?

  params.is_a?(Array) || params.is_a?(Hash)
end

#validate_json_rpc_call(item) ⇒ Object



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
# File 'lib/metarpc/interpreter.rb', line 50

def validate_json_rpc_call(item)
  # Containing the required key
  valid = item.key?('jsonrpc') && item.key?('method')

  # Does not contain unknown keys
  valid &&= (item.keys - %w[jsonrpc method params id]).empty?

  # Defining the correct JSONRPC version
  valid &&= item['jsonrpc'] == '2.0'

  # Defining a correct ID
  valid &&= valid_id(item['id'])

  # Defining a correct params object
  valid &&= valid_params(item['params'])

  # Defining a correct method name
  valid &&= valid_method_name(item['method'])

  raise_error(:invalid_request) unless valid

  item.deep_symbolize_keys.tap do |h|
    h[:method] = h[:method].to_sym
  end
end