Class: RestClient::Request

Inherits:
Object
  • Object
show all
Defined in:
lib/restclient/request.rb,
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


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/restclient/request.rb', line 34

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


24
25
26
# File 'lib/restclient/request.rb', line 24

def cookies
  @cookies
end

#headersObject (readonly)

Returns the value of attribute headers


24
25
26
# File 'lib/restclient/request.rb', line 24

def headers
  @headers
end

#methodObject (readonly)

Returns the value of attribute method


24
25
26
# File 'lib/restclient/request.rb', line 24

def method
  @method
end

#open_timeoutObject (readonly)

Returns the value of attribute open_timeout


24
25
26
# File 'lib/restclient/request.rb', line 24

def open_timeout
  @open_timeout
end

#passwordObject (readonly)

Returns the value of attribute password


24
25
26
# File 'lib/restclient/request.rb', line 24

def password
  @password
end

#payloadObject (readonly)

Returns the value of attribute payload


24
25
26
# File 'lib/restclient/request.rb', line 24

def payload
  @payload
end

#processed_headersObject (readonly)

Returns the value of attribute processed_headers


24
25
26
# File 'lib/restclient/request.rb', line 24

def processed_headers
  @processed_headers
end

#raw_responseObject (readonly)

Returns the value of attribute raw_response


24
25
26
# File 'lib/restclient/request.rb', line 24

def raw_response
  @raw_response
end

#ssl_ca_fileObject (readonly)

Returns the value of attribute ssl_ca_file


24
25
26
# File 'lib/restclient/request.rb', line 24

def ssl_ca_file
  @ssl_ca_file
end

#ssl_client_certObject (readonly)

Returns the value of attribute ssl_client_cert


24
25
26
# File 'lib/restclient/request.rb', line 24

def ssl_client_cert
  @ssl_client_cert
end

#ssl_client_keyObject (readonly)

Returns the value of attribute ssl_client_key


24
25
26
# File 'lib/restclient/request.rb', line 24

def ssl_client_key
  @ssl_client_key
end

#timeoutObject (readonly)

Returns the value of attribute timeout


24
25
26
# File 'lib/restclient/request.rb', line 24

def timeout
  @timeout
end

#urlObject (readonly)

Returns the value of attribute url


24
25
26
# File 'lib/restclient/request.rb', line 24

def url
  @url
end

#userObject (readonly)

Returns the value of attribute user


24
25
26
# File 'lib/restclient/request.rb', line 24

def user
  @user
end

#verify_sslObject (readonly)

Returns the value of attribute verify_ssl


24
25
26
# File 'lib/restclient/request.rb', line 24

def verify_ssl
  @verify_ssl
end

Class Method Details

.decode(content_encoding, body) ⇒ Object


228
229
230
231
232
233
234
235
236
# File 'lib/restclient/request.rb', line 228

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, &block) ⇒ Object


30
31
32
# File 'lib/restclient/request.rb', line 30

def self.execute(args, &block)
  new(args).execute &block
end

Instance Method Details

#default_headersObject


255
256
257
# File 'lib/restclient/request.rb', line 255

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

#execute(&block) ⇒ Object


53
54
55
56
57
58
59
60
61
62
# File 'lib/restclient/request.rb', line 53

def execute &block
  execute_inner &block
rescue Redirect => e
  @processed_headers.delete "Content-Length"
  @processed_headers.delete "Content-Type"
  @url = e.url
  @method = :get
  @payload = nil
  execute &block
end

#execute_inner(&block) ⇒ Object


64
65
66
67
# File 'lib/restclient/request.rb', line 64

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

#fetch_body(http_response) ⇒ Object


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/restclient/request.rb', line 172

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 RestClient.log
        if size == 0
          RestClient.log << "#{@method} #{@url} done (0 length file\n)"
        elsif total == 0
          RestClient.log << "#{@method} #{@url} (zero content length)\n"
        else
          RestClient.log << "#{@method} #{@url} %d%% done (%d of %d)\n" % [(size * 100) / total, size, total]
        end
      end
    end
    @tf.close
    @tf
  else
    http_response.read_body
  end
  http_response
end

#log_requestObject


238
239
240
241
242
243
244
245
246
# File 'lib/restclient/request.rb', line 238

def log_request
  if RestClient.log
    out = []
    out << "RestClient.#{method} #{url.inspect}"
    out << payload.short_inspect if payload
    out << processed_headers.inspect.gsub(/^\{/, '').gsub(/\}$/, '')
    RestClient.log << out.join(', ') + "\n"
  end
end

#log_response(res) ⇒ Object


248
249
250
251
252
253
# File 'lib/restclient/request.rb', line 248

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

#make_headers(user_headers) ⇒ Object


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
# File 'lib/restclient/request.rb', line 69

def make_headers user_headers
  unless @cookies.empty?
    user_headers[:cookie] = @cookies.map {|(key, val)| "#{key.to_s}=#{val}" }.sort.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


97
98
99
100
101
102
103
104
# File 'lib/restclient/request.rb', line 97

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


106
107
108
# File 'lib/restclient/request.rb', line 106

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

#parse_url(url) ⇒ Object


110
111
112
113
# File 'lib/restclient/request.rb', line 110

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

#parse_url_with_auth(url) ⇒ Object


115
116
117
118
119
120
# File 'lib/restclient/request.rb', line 115

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


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/restclient/request.rb', line 122

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


200
201
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
# File 'lib/restclient/request.rb', line 200

def process_result res
  if @raw_response    # We don't decode raw requests

    response = RawResponse.new(@tf, res)
  else
    response = Response.new(Request.decode(res['content-encoding'], res.body), res)
  end

  code = res.code.to_i

  if (301..303).include? code
    url = res.header['Location']

    if url !~ /^http/
      uri = URI.parse(@url)
      uri.path = "/#{url}".squeeze('/')
      url = uri.to_s
    end
    raise Redirect, url
  else
    if block_given?
      yield response
    else
      response.return!
    end
  end
end

#setup_credentials(req) ⇒ Object


168
169
170
# File 'lib/restclient/request.rb', line 168

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

#transmit(uri, req, payload, &block) ⇒ Object


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
# File 'lib/restclient/request.rb', line 139

def transmit uri, req, payload, &block
  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

  log_request

  net.start do |http|
    res = http.request(req, payload) { |http_response| fetch_body(http_response) }
    log_response res
    process_result res, &block
  end
rescue EOFError
  raise RestClient::ServerBrokeConnection
rescue Timeout::Error
  raise RestClient::RequestTimeout
end