Skip to content

Instantly share code, notes, and snippets.

@nickyp
Created August 8, 2012 13: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 nickyp/3294946 to your computer and use it in GitHub Desktop.
Save nickyp/3294946 to your computer and use it in GitHub Desktop.
Rest client stripped from an ancient Rails project ^_^
require 'net/http'
require 'uri'
require 'benchmark'
class Time
def to_Iso8601GmtDate
ConvertibleAttributes::Iso8601GmtDate.call(self)
end
end
class Logger
def info_timed(msg)
t = Time.now
info(t.to_Iso8601GmtDate + '.' + (t.usec/10000).to_s + ' ' + msg)
end
def debug_timed(msg)
t = Time.now
debug(t.to_Iso8601GmtDate + '.' + (t.usec/10000).to_s + ' ' + msg)
end
end
module Rest
class RestException < StandardError ; end ;
class ResourceNotFoundException < RestException ; end ;
class Client
# include Reloadable
cattr_accessor :logger
class << self
TEXT_S_EXPR = "text/s-expr"
def get(uri)
do_request(:get, uri)
end
def post(uri, body, content_type = TEXT_S_EXPR)
do_request(:post, uri, content_type, body)
end
def delete(uri)
do_request(:delete, uri)
end
def put(uri, body, content_type = TEXT_S_EXPR)
do_request(:put, uri, content_type, body)
end
private
@@http_object = nil
def http_reusable_object
return @@http_object unless @@http_object.nil?
logger.info_timed "\e[1;36;1mKeepAlive Connection \e[m started" if logger
@@http_object = Net::HTTP.new(HOST, PORT)
@@http_object.start
return @@http_object
end
def do_reusable_request(request)
begin
http_reusable_object.request(request)
rescue *ErrorsOnSocket
#logger.info_timed "\e[1;36;1mREUSABLE HTTP\e[m rescued! (#{$!})" if logger
if !@@http_object.nil?
@@http_object.finish
@@http_object = nil
end
retry
end
end
def do_request(method, uri, content_type = nil, body = nil, params = nil)
# escape the uri to remove spaces and other non uri character
uri = URI.escape(uri)
URI.parse(uri)
method_log = "\e[1;32;1mREST\e[m " # bold green
case method
when :get
method_log += "\e[1;33;1mGET\e[m"; # yellow
req = Net::HTTP::Get.new(uri)
when :post
method_log += "\e[1;37;1mPOST\e[m"; # white
req = Net::HTTP::Post.new(uri)
req.content_type = content_type
req.body = body
when :put
method_log += "\e[1;34;1mPUT\e[m"; # blue
req = Net::HTTP::Put.new(uri)
req.content_type = content_type
req.body = body
when :delete
method_log += "\e[0;37;1mDELETE\e[m"; # pink
req = Net::HTTP::Delete.new(uri)
else
raise "Invalid HTTP method specified"
end
http_runtime = [Benchmark::measure {
@res = do_reusable_request(req)
}.real, 0.0001].max
# Fancy log message after the HTTP request returned
logger.debug_timed method_log + " http://#{HOST}:#{PORT}#{uri} #{colorize_response_code(@res.code)}" if logger
# And resolve the result of our request
if (200..202).include?(@res.code.to_i)
if @res.body.nil? || @res.content_length == 0
return true
elsif @res.content_type == TEXT_S_EXPR && !@res.body.nil?
sexp_parse_runtime = [Benchmark::measure {
@sexp = Sexpression.parse_sexp(@res.body)
}.real, 0.0001].max
return @sexp
else
return @res.body
end
elsif @res.content_type == TEXT_S_EXPR
messages = extract_rest_exceptions(@res.body) unless @res.body.nil?
if @res.code.to_i == 404
raise ResourceNotFoundException.new(messages)
else
raise RestException.new(messages)
end
elsif @res.code == 404
raise ResourceNotFoundException(@res.error!)
else
raise "HTTP #{req.method} #{uri} &rarr; #{@res.code}"
end
ensure
# this ensures a nice log message with runtime statistics
if logger && http_runtime
sexp_parse_runtime ||= 0
runtime = http_runtime + sexp_parse_runtime
log_message = "Completed in #{sprintf("%.5f", runtime)} (#{(1 / runtime).floor} reqs/sec)"
log_message << format_rest_log_message("Http", runtime, http_runtime)
log_message << format_rest_log_message("Sexp", runtime, sexp_parse_runtime) unless sexp_parse_runtime.zero?
logger.debug_timed log_message
end
end
# Extracts exceptions out of HTTP body
def extract_rest_exceptions(body)
exceptions = Sexpression.parse_sexp(body)
messages = ""
exceptions.each_index { |i|
for error in ['message', 'code', 'description']
if exceptions[i].getf(error)
messages += exceptions[i].getf(error)
end
end
messages += ", " unless i = exceptions.size
}
logger.info_timed "\e[1;31;1mREST Error:\e[m #{messages}" if logger
return messages
end
private
def format_rest_log_message(label, total_runtime, partial_runtime)
return " | \e[1;35;1m#{label}:\e[m #{sprintf("%.5f", partial_runtime)} (#{sprintf("%d", (partial_runtime * 100) / total_runtime)}%)"
end
def colorize_response_code(code)
if (200..202).include?(code.to_i)
return "\e[0;34;1m#{code}\e[m"
else
return "\e[0;31;1m#{code}\e[m"
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment