Class: RestClient::Request

Inherits:
Object show all
Defined in:
lib/rest-client-1.2.0/lib/restclient/request.rb,
lib/rest-client-1.2.0/lib/restclient/exceptions.rb

Overview

backwards compatibility

Constant Summary collapse

Redirect =
RestClient::Redirect
Unauthorized =
RestClient::Unauthorized
RequestFailed =
RestClient::RequestFailed

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args) ⇒ Request

Returns a new instance of Request.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 32

def initialize(args)
  @method = args[:method] or raise ArgumentError, "must pass :method"
  @url = args[:url] or raise ArgumentError, "must pass :url"
  @headers = args[:headers] || {}
  @cookies = @headers.delete(:cookies) || args[:cookies] || {}
  @payload = Payload.generate(args[:payload])
  @user = args[:user]
  @password = args[:password]
  @timeout = args[:timeout]
  @open_timeout = args[:open_timeout]
  @raw_response = args[:raw_response] || false
  @verify_ssl = args[:verify_ssl] || false
  @ssl_client_cert = args[:ssl_client_cert] || nil
  @ssl_client_key = args[:ssl_client_key] || nil
  @ssl_ca_file = args[:ssl_ca_file] || nil
  @tf = nil # If you are a raw request, this is your tempfile
  @processed_headers = make_headers headers
end

Instance Attribute Details

#cookiesObject (readonly)

Returns the value of attribute cookies.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def cookies
  @cookies
end

#headersObject (readonly)

Returns the value of attribute headers.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def headers
  @headers
end

#methodObject (readonly)

Returns the value of attribute method.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def method
  @method
end

#open_timeoutObject (readonly)

Returns the value of attribute open_timeout.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def open_timeout
  @open_timeout
end

#passwordObject (readonly)

Returns the value of attribute password.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def password
  @password
end

#payloadObject (readonly)

Returns the value of attribute payload.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def payload
  @payload
end

#processed_headersObject (readonly)

Returns the value of attribute processed_headers.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def processed_headers
  @processed_headers
end

#raw_responseObject (readonly)

Returns the value of attribute raw_response.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def raw_response
  @raw_response
end

#ssl_ca_fileObject (readonly)

Returns the value of attribute ssl_ca_file.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def ssl_ca_file
  @ssl_ca_file
end

#ssl_client_certObject (readonly)

Returns the value of attribute ssl_client_cert.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def ssl_client_cert
  @ssl_client_cert
end

#ssl_client_keyObject (readonly)

Returns the value of attribute ssl_client_key.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def ssl_client_key
  @ssl_client_key
end

#timeoutObject (readonly)

Returns the value of attribute timeout.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def timeout
  @timeout
end

#urlObject (readonly)

Returns the value of attribute url.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def url
  @url
end

#userObject (readonly)

Returns the value of attribute user.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def user
  @user
end

#verify_sslObject (readonly)

Returns the value of attribute verify_ssl.



23
24
25
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 23

def verify_ssl
  @verify_ssl
end

Class Method Details

.decode(content_encoding, body) ⇒ Object



229
230
231
232
233
234
235
236
237
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 229

def self.decode(content_encoding, body)
  if content_encoding == 'gzip' and not body.empty?
    Zlib::GzipReader.new(StringIO.new(body)).read
  elsif content_encoding == 'deflate'
    Zlib::Inflate.new.inflate(body)
  else
    body
  end
end

.execute(args) ⇒ Object



28
29
30
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 28

def self.execute(args)
  new(args).execute
end

Instance Method Details

#default_headersObject



266
267
268
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 266

def default_headers
  { :accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate' }
end

#display_log(msg) ⇒ Object



254
255
256
257
258
259
260
261
262
263
264
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 254

def display_log(msg)
  return unless log_to = RestClient.log

  if log_to == 'stdout'
    STDOUT.puts msg
  elsif log_to == 'stderr'
    STDERR.puts msg
  else
    File.open(log_to, 'a') { |f| f.puts msg }
  end
end

#executeObject



51
52
53
54
55
56
57
58
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 51

def execute
  execute_inner
rescue Redirect => e
  @url = e.url
  @method = :get
  @payload = nil
  execute
end

#execute_innerObject



60
61
62
63
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 60

def execute_inner
  uri = parse_url_with_auth(url)
  transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload
end

#fetch_body(http_response) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 176

def fetch_body(http_response)
  if @raw_response
    # Taken from Chef, which as in turn...
    # Stolen from http://www.ruby-forum.com/topic/166423
    # Kudos to _why!
    @tf = Tempfile.new("rest-client")
    size, total = 0, http_response.header['Content-Length'].to_i
    http_response.read_body do |chunk|
      @tf.write(chunk)
      size += chunk.size
      if size == 0
        display_log("#{@method} #{@url} done (0 length file)")
      elsif total == 0
        display_log("#{@method} #{@url} (zero content length)")
      else
        display_log("#{@method} #{@url} %d%% done (%d of %d)" % [(size * 100) / total, size, total])
      end
    end
    @tf.close
    @tf
  else
    http_response.read_body
  end
  http_response
end

#make_headers(user_headers) ⇒ Object



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
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 65

def make_headers user_headers
  unless @cookies.empty?
    user_headers[:cookie] = @cookies.map {|key, val| "#{key.to_s}=#{val}" }.join('; ')
  end

  headers = default_headers.merge(user_headers).inject({}) do |final, (key, value)|
    target_key = key.to_s.gsub(/_/, '-').capitalize
    if 'CONTENT-TYPE' == target_key.upcase
      target_value = value.to_s
      final[target_key] = MIME::Types.type_for_extension target_value
    elsif 'ACCEPT' == target_key.upcase
        # Accept can be composed of several comma-separated values
        if value.is_a? Array
          target_values = value
        else
          target_values = value.to_s.split ','
        end
        final[target_key] = target_values.map{ |ext| MIME::Types.type_for_extension(ext.to_s.strip)}.join(', ')
    else
      final[target_key] = value.to_s
    end
    final
  end

  headers.merge!(@payload.headers) if @payload
  headers
end

#net_http_classObject



93
94
95
96
97
98
99
100
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 93

def net_http_class
  if RestClient.proxy
    proxy_uri = URI.parse(RestClient.proxy)
    Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
  else
    Net::HTTP
  end
end

#net_http_request_class(method) ⇒ Object



102
103
104
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 102

def net_http_request_class(method)
  Net::HTTP.const_get(method.to_s.capitalize)
end

#parse_url(url) ⇒ Object



106
107
108
109
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 106

def parse_url(url)
  url = "http://#{url}" unless url.match(/^http/)
  URI.parse(url)
end

#parse_url_with_auth(url) ⇒ Object



111
112
113
114
115
116
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 111

def parse_url_with_auth(url)
  uri = parse_url(url)
  @user = uri.user if uri.user
  @password = uri.password if uri.password
  uri
end

#process_payload(p = nil, parent_key = nil) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 118

def process_payload(p=nil, parent_key=nil)
  unless p.is_a?(Hash)
    p
  else
    @headers[:content_type] ||= 'application/x-www-form-urlencoded'
    p.keys.map do |k|
      key = parent_key ? "#{parent_key}[#{k}]" : k
      if p[k].is_a? Hash
        process_payload(p[k], key)
      else
        value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
        "#{key}=#{value}"
      end
    end.join("&")
  end
end

#process_result(res) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 202

def process_result(res)
  if res.code =~ /\A2\d{2}\z/
    # We don't decode raw requests
    unless @raw_response
      self.class.decode res['content-encoding'], res.body if res.body
    end
  elsif %w(301 302 303).include? res.code
    url = res.header['Location']

    if url !~ /^http/
      uri = URI.parse(@url)
      uri.path = "/#{url}".squeeze('/')
      url = uri.to_s
    end

    raise Redirect, url
  elsif res.code == "304"
    raise NotModified, res
  elsif res.code == "401"
    raise Unauthorized, res
  elsif res.code == "404"
    raise ResourceNotFound, res
  else
    raise RequestFailed, res
  end
end

#request_logObject



239
240
241
242
243
244
245
246
247
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 239

def request_log
  if RestClient.log
    out = []
    out << "RestClient.#{method} #{url.inspect}"
    out << "headers: #{processed_headers.inspect}"
    out << "paylod: #{payload.short_inspect}" if payload
    out.join(', ')
  end
end

#response_log(res) ⇒ Object



249
250
251
252
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 249

def response_log(res)
  size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
  "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes"
end

#setup_credentials(req) ⇒ Object



172
173
174
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 172

def setup_credentials(req)
  req.basic_auth(user, password) if user
end

#transmit(uri, req, payload) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/rest-client-1.2.0/lib/restclient/request.rb', line 135

def transmit(uri, req, payload)
  setup_credentials(req)

  net = net_http_class.new(uri.host, uri.port)
  net.use_ssl = uri.is_a?(URI::HTTPS)
  if @verify_ssl == false
    net.verify_mode = OpenSSL::SSL::VERIFY_NONE
  elsif @verify_ssl.is_a? Integer
    net.verify_mode = @verify_ssl
  end
  net.cert = @ssl_client_cert if @ssl_client_cert
  net.key = @ssl_client_key if @ssl_client_key
  net.ca_file = @ssl_ca_file if @ssl_ca_file
  net.read_timeout = @timeout if @timeout
  net.open_timeout = @open_timeout if @open_timeout

  display_log request_log

  net.start do |http|
    res = http.request(req, payload) { |http_response| fetch_body(http_response) }
    result = process_result(res)
    display_log response_log(res)

    if result.kind_of?(String) or @method == :head
      Response.new(result, res)
    elsif @raw_response
      RawResponse.new(@tf, res)
    else
      Response.new(nil, res)
    end
  end
rescue EOFError
  raise RestClient::ServerBrokeConnection
rescue Timeout::Error
  raise RestClient::RequestTimeout
end