aboutsummaryrefslogtreecommitdiffstats
path: root/activeresource/lib/active_resource/connection.rb
blob: 1a1b376d3af8c7ba0f7dd73bb3231b9b92e7d074 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
require 'net/https'
require 'date'
require 'time'
require 'uri'
require 'benchmark'

module ActiveResource
  class ConnectionError < StandardError
    attr_reader :response

    def initialize(response, message = nil)
      @response = response
      @message  = message
    end

    def to_s
      "Failed with #{response.code}"
    end
  end

  class ClientError < ConnectionError;  end  # 4xx Client Error
  class ResourceNotFound < ClientError; end  # 404 Not Found
  class ResourceConflict < ClientError; end  # 409 Conflict

  class ServerError < ConnectionError;  end  # 5xx Server Error


  class Connection
    attr_reader :site

    class << self
      def requests
        @@requests ||= []
      end
      
      def default_header
        class << self ; attr_reader :default_header end
        @default_header = { 'Content-Type' => 'application/xml' }
      end
    end

    def initialize(site)
      self.site = site.is_a?(URI) ? site : URI.parse(site)
    end
    
    def site=(site)
      @site = site.is_a?(URI) ? site : URI.parse(site)
    end

    def get(path)
      Hash.from_xml(request(:get, path, build_request_headers).body)
    end

    def delete(path)
      request(:delete, path, build_request_headers)
    end

    def put(path, body = '')
      request(:put, path, body, build_request_headers)
    end

    def post(path, body = '')
      request(:post, path, body, build_request_headers)
    end

    private
      def request(method, path, *arguments)
        logger.info "requesting #{method.to_s.upcase} #{site.scheme}://#{site.host}:#{site.port}#{path}" if logger
        result = nil
        time = Benchmark.realtime { result = http.send(method, path, *arguments) }
        logger.info "--> #{result.code} #{result.message} (#{result.body.length}b %.2fs)" % time if logger
        handle_response(result)
      end

      def handle_response(response)
        case response.code.to_i
          when 200...400
            response
          when 404
            raise(ResourceNotFound.new(response))
          when 400
            raise(ResourceInvalid.new(response))
          when 409
            raise(ResourceConflict.new(response))
          when 401...500
            raise(ClientError.new(response))
          when 500...600
            raise(ServerError.new(response))
          else
            raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
        end
      end

      def http
        unless @http
          @http             = Net::HTTP.new(@site.host, @site.port)
          @http.use_ssl     = @site.is_a?(URI::HTTPS)
          @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @http.use_ssl
        end

        @http
      end
      
      def build_request_headers
        authorization_header.update(self.class.default_header)
      end
      
      def authorization_header
        (@site.user || @site.password ? { 'Authorization' => 'Basic ' + ["#{@site.user}:#{ @site.password}"].pack('m').delete("\r\n") } : {})
      end

      def logger
        ActiveResource::Base.logger
      end
  end
end