module Restfulie module Client module HTTP #:nodoc: # Request Adapter provides a minimal interface to exchange information between server over HTTP protocol through simple adapters. # # All the concrete adapters follow the interface laid down in this module. # Default connection provider is net/http # #==Example # # @re = ::Restfulie::Client::HTTP::RequestExecutor.new('http://restfulie.com') #this class includes RequestAdapter module. # puts @re.as('application/atom+xml').get!('/posts').title #=> 'Hello World!' # module RequestAdapter attr_reader :host attr_accessor :cookies attr_writer :default_headers def host=(host) if host.is_a?(::URI) @host = host else @host = ::URI.parse(host) end end def default_headers @default_headers ||= {} end # GET HTTP verb without {Error} # * path: '/posts' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def get(path, *args) request(:get, path, *args) end # HEAD HTTP verb without {Error} # * path: '/posts' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def head(path, *args) request(:head, path, *args) end # POST HTTP verb without {Error} # * path: '/posts' # * payload: 'some text' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def post(path, payload, *args) request(:post, path, payload, *args) end # PATCH HTTP verb without {Error} # * path: '/posts' # * payload: 'some text' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def patch(path, payload, *args) request(:patch, path, payload, *args) end # PUT HTTP verb without {Error} # * path: '/posts' # * payload: 'some text' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def put(path, payload, *args) request(:put, path, payload, *args) end # DELETE HTTP verb without {Error} # * path: '/posts' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def delete(path, *args) request(:delete, path, *args) end # GET HTTP verb {Error} # * path: '/posts' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def get!(path, *args) request!(:get, path, *args) end # HEAD HTTP verb {Error} # * path: '/posts' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def head!(path, *args) request!(:head, path, *args) end # POST HTTP verb {Error} # * path: '/posts' # * payload: 'some text' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def post!(path, payload, *args) request!(:post, path, payload, *args) end # PATCH HTTP verb {Error} # * path: '/posts' # * payload: 'some text' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def patch!(path, payload, *args) request!(:patch, path, payload, *args) end # PUT HTTP verb {Error} # * path: '/posts' # * payload: 'some text' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def put!(path, payload, *args) request!(:put, path, payload, *args) end # DELETE HTTP verb {Error} # * path: '/posts' # * headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def delete!(path, *args) request!(:delete, path, *args) end # Executes a request against your server and return a response instance without {Error} # * method: :get,:post,:delete,:head,:put # * path: '/posts' # * args: payload: 'some text' and/or headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def request(method, path, *args) request!(method, path, *args) rescue Error::RESTError => se se.response end # Executes a request against your server and return a response instance. # * method: :get,:post,:delete,:head,:put # * path: '/posts' # * args: payload: 'some text' and/or headers: {'Accept' => '*/*', 'Content-Type' => 'application/atom+xml'} def request!(method, path, *args) headers = default_headers.merge(args.extract_options!) unless @host.user.blank? && @host.password.blank? headers["Authorization"] = "Basic " + ["#{@host.user}:#{@host.password}"].pack("m").delete("\r\n") end headers['cookie'] = @cookies if @cookies args << headers ::Restfulie::Common::Logger.logger.info(request_to_s(method, path, *args)) if ::Restfulie::Common::Logger.logger begin response = ResponseHandler.handle(method, path, get_connection_provider.send(method, path, *args)) rescue Exception => e raise Error::ServerNotAvailableError.new(self, Response.new(method, path, 503, nil, {}), e ) end case response.code when 100..299 response when 300..399 raise Error::Redirection.new(self, response) when 400 raise Error::BadRequest.new(self, response) when 401 raise Error::Unauthorized.new(self, response) when 403 raise Error::Forbidden.new(self, response) when 404 raise Error::NotFound.new(self, response) when 405 raise Error::MethodNotAllowed.new(self, response) when 407 raise Error::ProxyAuthenticationRequired.new(self, response) when 409 raise Error::Conflict.new(self, response) when 410 raise Error::Gone.new(self, response) when 412 raise Error::PreconditionFailed.new(self, response) when 402, 406, 408, 411, 413..499 raise Error::ClientError.new(self, response) when 501 raise Error::NotImplemented.new(self, response) when 500, 502..599 raise Error::ServerError.new(self, response) else raise Error::UnknownError.new(self, response) end end private def get_connection_provider @connection ||= ::Net::HTTP.new(@host.host, @host.port) end protected def request_to_s(method, path, *args) result = ["#{method.to_s.upcase} #{path}"] arguments = args.dup headers = arguments.extract_options! if [:post, :put].include?(method) body = arguments.shift end result << headers.collect { |key, value| "#{key}: #{value}" }.join("\n") (result + [body ? (body.inspect + "\n") : nil]).compact.join("\n") << "\n" end end end end end