Created
August 8, 2012 13:09
-
-
Save nickyp/3294946 to your computer and use it in GitHub Desktop.
Rest client stripped from an ancient Rails project ^_^
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 && [email protected]? | |
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} → #{@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