require 'net/http' require 'net/https' require 'json' require 'cgi' require 'pp' require 'uri' class Fastly # The UserAgent to communicate with the API class Client #:nodoc: all begin require 'curb-fu' CURB_FU=true rescue LoadError CURB_FU=false end attr_accessor :http, :api_key, :user, :password, :cookie, :customer def initialize(opts) [:api_key, :user, :password].each do |key| self.send("#{key}=", opts[key]) if opts.has_key?(key) end base = opts[:base_url] || "https://api.fastly.com" uri = URI.parse(base) scheme = uri.scheme host = uri.host curb = opts.has_key?(:use_curb) ? !!opts[:use_curb] && CURB_FU : CURB_FU port = opts.has_key?(:base_port) ? opts[:base_port] : (scheme == "https") ? 443 : 80 self.http = curb ? Fastly::Client::Curl.new(host, port) : Net::HTTP.new(host, port) self.http.use_ssl = (scheme == "https") return self unless fully_authed? # If we're fully authed (i.e username and password ) then we need to log in resp = self.http.post('/login', make_params(:user => user, :password => password)) raise Fastly::Unauthorized unless resp.success? self.cookie = resp['set-cookie'] end def require_key! raise Fastly::AuthRequired.new("This request requires an API key") if api_key.nil? @require_key = true end def require_key? !!@require_key end def authed? !api_key.nil? || fully_authed? end # Some methods require full username and password rather than just auth token def fully_authed? !(user.nil? || password.nil?) end def set_customer(id) end def get(path, params={}) path += "?"+make_params(params) unless params.empty? resp = self.http.get(path, headers) return nil if 404 == resp.status raise Fastly::Error, resp.message unless resp.success? JSON.parse(resp.body) end def get_stats(path, params={}) content = get(path, params) raise Fastly::Error, content["message"] unless content["status"] == 'success' content["data"] end def post(path, params={}) post_and_put(:post, path, params) end def put(path, params={}) post_and_put(:put, path, params) end def delete(path) resp = self.http.delete(path, headers) return resp.success? end private def post_and_put(method, path, params={}) query = make_params(params) resp = self.http.send(method, path, query, headers.merge( 'Content-Type' => "application/x-www-form-urlencoded")) raise Fastly::Error, resp.message unless resp.success? JSON.parse(resp.body) end def headers headers = if require_key? api_key_header else fully_authed? ? { 'Cookie' => cookie } : api_key_header end headers.merge!('Fastly-Explicit-Customer' => customer) if customer headers.merge!('Content-Accept' => 'application/json') ensure @require_key = nil end def api_key_header { 'X-Fastly-Key' => api_key } end def make_params(params) params.map { |key,val| next if val.nil? unless val.is_a?(Hash) "#{CGI.escape(key.to_s)}=#{CGI.escape(val.to_s)}" else val.map { |sub_key, sub_val| new_key = "#{key}[#{sub_key}]" "#{CGI.escape(new_key)}=#{CGI.escape(sub_val.to_s)}" } end }.flatten.delete_if { |v| v.nil? }.join("&") end # :nodoc: all class Curl attr_accessor :host, :port, :protocol def initialize(host, port=443) self.host = host self.port = port self.protocol = 'https' end def get(path, headers={}) CurbFu.get({ :host => host, :port => port, :path => path, :headers => headers, :protocol => protocol }) end def post(path, params, headers={}) CurbFu.post({ :host => host, :port => port, :path => path, :headers => headers, :protocol => protocol }, params) end def put(path, params, headers={}) CurbFu.put({ :host => host, :port => port, :path => path, :headers => headers, :params => params, :protocol => protocol }, params) end def delete(path, headers={}) CurbFu.delete({ :host => host, :port => port, :path => path, :headers => headers, :protocol => protocol }) end def use_ssl=(ssl) self.protocol = ssl ? 'https' : 'http' end end end end # :nodoc: all class Net::HTTPResponse def success? return Net::HTTPSuccess === self end def status return self.code.to_i end end # :nodoc: all class CurbFu::Response::Base def get_fields(key) if ( match = @headers.find{|k,v| k.downcase == key.downcase} ) [match.last].flatten else [] end end def [](key) get_fields(key).last end end