lib/postmark/http_client.rb
require 'thread' unless defined? Mutex # For Ruby 1.8.7
require 'cgi'
module Postmark
class HttpClient
attr_accessor :api_token
attr_reader :http, :secure, :proxy_host, :proxy_port, :proxy_user,
:proxy_pass, :host, :port, :path_prefix,
:http_open_timeout, :http_read_timeout, :http_ssl_version,
:auth_header_name
alias_method :api_key, :api_token
alias_method :api_key=, :api_token=
DEFAULTS = {
:auth_header_name => 'X-Postmark-Server-Token',
:host => 'api.postmarkapp.com',
:secure => true,
:path_prefix => '/',
:http_read_timeout => 60,
:http_open_timeout => 60
}
def initialize(api_token, options = {})
@api_token = api_token
@request_mutex = Mutex.new
apply_options(options)
@http = build_http
end
def post(path, data = '')
do_request { |client| client.post(url_path(path), data, headers) }
end
def put(path, data = '')
do_request { |client| client.put(url_path(path), data, headers) }
end
def patch(path, data = '')
do_request { |client| client.patch(url_path(path), data, headers) }
end
def get(path, query = {})
do_request { |client| client.get(url_path(path + to_query_string(query)), headers) }
end
def delete(path, query = {})
do_request { |client| client.delete(url_path(path + to_query_string(query)), headers) }
end
def protocol
self.secure ? 'https' : 'http'
end
protected
def apply_options(options = {})
options = Hash[*options.select { |_, v| !v.nil? }.flatten]
DEFAULTS.merge(options).each_pair do |name, value|
instance_variable_set(:"@#{name}", value)
end
@port = options[:port] || (@secure ? 443 : 80)
end
def to_query_string(hash)
return "" if hash.empty?
"?" + hash.map { |key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}" }.join("&")
end
def url
URI.parse("#{protocol}://#{self.host}:#{self.port}/")
end
def handle_response(response)
if response.code.to_i == 200
Postmark::Json.decode(response.body)
else
raise HttpServerError.build(response.code, response.body)
end
end
def headers
HEADERS.merge(self.auth_header_name => self.api_token.to_s)
end
def url_path(path)
self.path_prefix + path
end
def do_request
@request_mutex.synchronize do
handle_response(yield(http))
end
rescue Timeout::Error => e
raise TimeoutError.new(e)
rescue Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError => e
raise HttpClientError.new(e.message)
end
def build_http
http = Net::HTTP::Proxy(self.proxy_host,
self.proxy_port,
self.proxy_user,
self.proxy_pass).new(url.host, url.port)
http.read_timeout = self.http_read_timeout
http.open_timeout = self.http_open_timeout
http.use_ssl = !!self.secure
http.ssl_version = self.http_ssl_version if self.http_ssl_version && http.respond_to?(:ssl_version=)
http
end
end
end