#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require 'json'
module RPC
class InvalidRequestError < StandardError; end
class UndefinedMethodError < StandardError; end
class Request
attr_reader :method, :params, :id
def initialize(method, *args)
options = args.last.is_a?(Hash) ? args.pop : {}
@method = method
@params = args
@id = options[:id]
end
def to_json
JSON.dump({:method => @method, :params => @params, :id => @id})
end
def self.parse(str)
json = JSON.parse(str) rescue {}
method, params, id = json.values_at('method', 'params', 'id')
raise InvalidRequestError if method.nil?
self.new(method, *(params || []), :id => id)
end
end
class Response
attr_reader :result, :error, :id
def initialize(result, *args)
options = args.last.is_a?(Hash) ? args.pop : {}
@result = result
@error = nil
@result, @error = @error, @result if options[:error]
@id = options[:id]
end
def to_json
JSON.dump({:result => @result, :error => @error, :id => @id})
end
end
module Receiver
def self.included(base)
base.send :include, InstanceMethods
base.send :extend, ClassMethods
end
module ClassMethods
def attr_rpc(*methods)
methods.each do |method|
rpc_methods << method.to_s
end
rpc_methods.uniq!
end
def rpc_methods
@rpc_methods ||= []
end
end
module InstanceMethods
def process_request(data)
begin
request = Request.parse(data)
raise UndefinedMethodError unless self.class.rpc_methods.include?(request.method)
id = request.id
result = self.send(request.method, *request.params)
error = false
rescue => e
id = nil
result = e.to_s
error = true
ensure
return Response.new(result, :id => id, :error => error)
end
end
end
end
end
class Server
include RPC::Receiver
attr_rpc :mul
def mul(a, b)
a.to_i * b.to_i
end
end
server = Server.new
req = RPC::Request.new('mul', 3, 4, :id => Time.now.to_i)
puts req.to_json
res = server.process_request(req.to_json)
puts res.to_json
IyEvdXNyL2Jpbi9lbnYgcnVieQojIC0qLSBjb2Rpbmc6IHV0Zi04IC0qLQpyZXF1aXJlICdqc29uJwoKbW9kdWxlIFJQQwogIGNsYXNzIEludmFsaWRSZXF1ZXN0RXJyb3IgPCBTdGFuZGFyZEVycm9yOyBlbmQKICBjbGFzcyBVbmRlZmluZWRNZXRob2RFcnJvciA8IFN0YW5kYXJkRXJyb3I7IGVuZAoKICBjbGFzcyBSZXF1ZXN0CiAgICBhdHRyX3JlYWRlciA6bWV0aG9kLCA6cGFyYW1zLCA6aWQKCiAgICBkZWYgaW5pdGlhbGl6ZShtZXRob2QsICphcmdzKQogICAgICBvcHRpb25zID0gYXJncy5sYXN0LmlzX2E/KEhhc2gpID8gYXJncy5wb3AgOiB7fQogICAgICBAbWV0aG9kID0gbWV0aG9kCiAgICAgIEBwYXJhbXMgPSBhcmdzCiAgICAgIEBpZCA9IG9wdGlvbnNbOmlkXQogICAgZW5kCgogICAgZGVmIHRvX2pzb24KICAgICAgSlNPTi5kdW1wKHs6bWV0aG9kID0+IEBtZXRob2QsIDpwYXJhbXMgPT4gQHBhcmFtcywgOmlkID0+IEBpZH0pCiAgICBlbmQKCiAgICBkZWYgc2VsZi5wYXJzZShzdHIpCiAgICAgIGpzb24gPSBKU09OLnBhcnNlKHN0cikgcmVzY3VlIHt9CiAgICAgIG1ldGhvZCwgcGFyYW1zLCBpZCA9IGpzb24udmFsdWVzX2F0KCdtZXRob2QnLCAncGFyYW1zJywgJ2lkJykKICAgICAgcmFpc2UgSW52YWxpZFJlcXVlc3RFcnJvciBpZiBtZXRob2QubmlsPwogICAgICBzZWxmLm5ldyhtZXRob2QsICoocGFyYW1zIHx8IFtdKSwgOmlkID0+IGlkKQogICAgZW5kCiAgZW5kCgogIGNsYXNzIFJlc3BvbnNlCiAgICBhdHRyX3JlYWRlciA6cmVzdWx0LCA6ZXJyb3IsIDppZAoKICAgIGRlZiBpbml0aWFsaXplKHJlc3VsdCwgKmFyZ3MpCiAgICAgIG9wdGlvbnMgPSBhcmdzLmxhc3QuaXNfYT8oSGFzaCkgPyBhcmdzLnBvcCA6IHt9CiAgICAgIEByZXN1bHQgPSByZXN1bHQKICAgICAgQGVycm9yID0gbmlsCiAgICAgIEByZXN1bHQsIEBlcnJvciA9IEBlcnJvciwgQHJlc3VsdCBpZiBvcHRpb25zWzplcnJvcl0KICAgICAgQGlkID0gb3B0aW9uc1s6aWRdCiAgICBlbmQKCiAgICBkZWYgdG9fanNvbgogICAgICBKU09OLmR1bXAoezpyZXN1bHQgPT4gQHJlc3VsdCwgOmVycm9yID0+IEBlcnJvciwgOmlkID0+IEBpZH0pCiAgICBlbmQKICBlbmQKCiAgbW9kdWxlIFJlY2VpdmVyCiAgICBkZWYgc2VsZi5pbmNsdWRlZChiYXNlKQogICAgICBiYXNlLnNlbmQgOmluY2x1ZGUsIEluc3RhbmNlTWV0aG9kcwogICAgICBiYXNlLnNlbmQgOmV4dGVuZCwgQ2xhc3NNZXRob2RzCiAgICBlbmQKCiAgICBtb2R1bGUgQ2xhc3NNZXRob2RzCiAgICAgIGRlZiBhdHRyX3JwYygqbWV0aG9kcykKICAgICAgICBtZXRob2RzLmVhY2ggZG8gfG1ldGhvZHwKICAgICAgICAgIHJwY19tZXRob2RzIDw8IG1ldGhvZC50b19zCiAgICAgICAgZW5kCiAgICAgICAgcnBjX21ldGhvZHMudW5pcSEKICAgICAgZW5kCiAgICAgIAogICAgICBkZWYgcnBjX21ldGhvZHMKICAgICAgICBAcnBjX21ldGhvZHMgfHw9IFtdCiAgICAgIGVuZAogICAgZW5kCgogICAgbW9kdWxlIEluc3RhbmNlTWV0aG9kcwogICAgICBkZWYgcHJvY2Vzc19yZXF1ZXN0KGRhdGEpCiAgICAgICAgYmVnaW4KICAgICAgICAgIHJlcXVlc3QgPSBSZXF1ZXN0LnBhcnNlKGRhdGEpCiAgICAgICAgICByYWlzZSBVbmRlZmluZWRNZXRob2RFcnJvciB1bmxlc3Mgc2VsZi5jbGFzcy5ycGNfbWV0aG9kcy5pbmNsdWRlPyhyZXF1ZXN0Lm1ldGhvZCkKICAgICAgICAgIGlkID0gcmVxdWVzdC5pZAogICAgICAgICAgcmVzdWx0ID0gc2VsZi5zZW5kKHJlcXVlc3QubWV0aG9kLCAqcmVxdWVzdC5wYXJhbXMpCiAgICAgICAgICBlcnJvciA9IGZhbHNlCiAgICAgICAgcmVzY3VlID0+IGUKICAgICAgICAgIGlkID0gbmlsCiAgICAgICAgICByZXN1bHQgPSBlLnRvX3MKICAgICAgICAgIGVycm9yID0gdHJ1ZQogICAgICAgIGVuc3VyZQogICAgICAgICAgcmV0dXJuIFJlc3BvbnNlLm5ldyhyZXN1bHQsIDppZCA9PiBpZCwgOmVycm9yID0+IGVycm9yKQogICAgICAgIGVuZAogICAgICBlbmQKICAgIGVuZAogIGVuZAplbmQKCmNsYXNzIFNlcnZlcgogIGluY2x1ZGUgUlBDOjpSZWNlaXZlcgogIGF0dHJfcnBjIDptdWwKCiAgZGVmIG11bChhLCBiKQogICAgYS50b19pICogYi50b19pCiAgZW5kCmVuZAoKc2VydmVyID0gU2VydmVyLm5ldwpyZXEgPSBSUEM6OlJlcXVlc3QubmV3KCdtdWwnLCAzLCA0LCA6aWQgPT4gVGltZS5ub3cudG9faSkKcHV0cyByZXEudG9fanNvbgpyZXMgPSBzZXJ2ZXIucHJvY2Vzc19yZXF1ZXN0KHJlcS50b19qc29uKQpwdXRzIHJlcy50b19qc29u