Skip to content

Instantly share code, notes, and snippets.

@ahoward
Created January 31, 2018 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ahoward/63e0b925d6e984155e77a34ce3d927a5 to your computer and use it in GitHub Desktop.
Save ahoward/63e0b925d6e984155e77a34ce3d927a5 to your computer and use it in GitHub Desktop.
all the ruby json rpc gems suck so i made this teeny one. put it in lib/json_rpc.rb
# ref: http://www.jsonrpc.org/specification
#
# stdlibs
#
require 'net/http'
# gems
#
require 'addressable/uri'
require 'json'
require 'map'
require 'fattr'
require 'logger'
require 'timeout'
require 'openssl'
require 'securerandom'
#
Object.send(:remove_const, :JsonRPC) if defined?(JsonRPC)
#
class JsonRPC
#
class Error < ::StandardError
fattr(:raw)
def message
[super, raw].compact.join("\n\n@raw:\n\n")
end
end
#
class Client
def initialize(url, *args, &block)
@uri = Addressable::URI.parse(url.to_s)
@options = Map.extract_options!(args)
@headers = Map.for(@options[:headers] || {})
end
def call(method, *args)
method = method.to_s.strip
params =
case
when args.size == 1 && args.last.is_a?(Hash)
args.last
when args.size == 1 && args.last.is_a?(Array)
args.last
else
args
end
id = SecureRandom.uuid
data = {
:jsonrpc => '2.0',
:id => id,
:method => method,
:params => params,
}
uri = @uri.dup
headers = @headers.merge({
'Content-Type' =>'application/json'
})
n = 42
request = nil
response = nil
result = nil
begin
http = Net::HTTP.new(uri.host, uri.port)
case uri.scheme
when 'https'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.body = data.to_json
response = debugging_http(http){ http.request(request) }
rescue SocketError, Timeout::Error
n -= 1
return nil if n <= 0
sleep(rand)
retry
end
unless response.is_a?(Net::HTTPSuccess)
error!("failed with: url=#{ url }, path=#{ path }, headers=#{ headers.to_json}, json=#{ json }")
end
body = response.body
result = JSON.parse(body)
if result.has_key?("error")
raise(JsonRPC::Error, result.inspect)
end
result["result"]
end
#
def uri_for(*args, &block)
options = Map.options_for!(args)
path_info = absolute_path_for(args.join('/') || options[:path_info])
query = Map.for(options[:query] || {})
uri = self.uri.dup
unless path_info.to_s.strip.empty?
uri.path = absolute_path_for(uri.path, path_info)
end
uri
end
#
def url_for(*args, &block)
uri_for(*args, &block).to_s
end
#
def paths_for(*args)
path = args.flatten.compact.join('/')
path.gsub!(%r|[.]+/|, '/')
path.squeeze!('/')
path.sub!(%r|^/|, '')
path.sub!(%r|/$|, '')
paths = path.split('/')
end
def absolute_path_for(*args)
path = ('/' + paths_for(*args).join('/')).squeeze('/')
path unless path.to_s.strip.empty?
end
def relative_path_for(*args)
path = absolute_path_for(*args).sub(%r{^/+}, '')
path unless path.to_s.strip.empty?
end
#
def post(url, data ={})
uri = url.is_a?(URI) ? url : URI.parse(url.to_s)
n = 42
begin
http = Net::HTTP.new(uri.host, uri.port)
case uri.scheme
when 'https'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
headers = {'Content-Type' =>'application/json'}
request = Net::HTTP::Post.new(uri.request_uri, headers)
request.body = data.to_json
response = debugging_http(http){ http.request(request) }
rescue SocketError, Timeout::Error
n -= 1
return nil if n <= 0
sleep(rand)
retry
end
end
#
def get(url)
uri = url.is_a?(URI) ? URI.parse(url.to_s) : url
n = 42
begin
http = Net::HTTP.new(uri.host, uri.port)
case uri.scheme
when 'https'
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
headers = {'Content-Type' =>'application/json'}
request = Net::HTTP::Get.new(uri.request_uri, headers)
response = debugging_http(http){ http.request(request) }
rescue SocketError, TimeoutError
n -= 1
return nil if n <= 0
sleep(rand)
retry
end
end
#
fattr(:debug_log){ Array.new }
#
def debug(*args, &block)
if defined?(@debug)
@debug
else
@debug =
case
when ENV['JSONRPC_DEBUG']
Coerce.boolean(ENV['JSONRPC_DEBUG'])
else
rails_logger
end
end
if((args.size>0) || block)
debug!(*args, &block)
else
@debug
end
end
#
def debug=(debug)
case
when debug.respond_to?(:<<)
@debug = debug
when debug == true
@debug = rails_logger
else
@debug = debug
end
end
def rails_logger
defined?(Rails) ? Rails.logger : Logger.new(STDERR)
end
#
def debug?
!!debug
end
#
def debugging_http(http, &block)
return block.call unless debug?
sio = StringIO.new
http.set_debug_output(sio)
begin
block.call
ensure
sio.rewind
raw = sio.read
debug_log.push(raw)
debug!(raw)
end
end
#
def debug!(*args, &block)
msg = args.flatten.compact.join(' ')
if block
msg << Array(block.call).flatten.compact.join(' ')
end
msg.strip!
return false if msg.to_s.strip.empty?
case
when debug.respond_to?(:info)
debug.info(msg)
when debug.respond_to?(:<<)
debug << msg
when debug.respond_to?(:write)
debug.write(msg)
if debug.respond_to?(:flush)
debug.flush
end
else
$stderr.puts(msg)
end
end
#
def error!(*args, &block)
error = Error.new(*args, &block)
raise(error)
ensure
error.raw = debug_log.last
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment