rest-client_jxb_fix 1.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,55 @@
1
+ module Net
2
+ class HTTP
3
+
4
+ # Adding the patch method if it doesn't exist (rest-client issue: https://github.com/archiloque/rest-client/issues/79)
5
+ if !defined?(Net::HTTP::Patch)
6
+ # Code taken from this commit: https://github.com/ruby/ruby/commit/ab70e53ac3b5102d4ecbe8f38d4f76afad29d37d#lib/net/http.rb
7
+ class Protocol
8
+ # Sends a PATCH request to the +path+ and gets a response,
9
+ # as an HTTPResponse object.
10
+ def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+
11
+ send_entity(path, data, initheader, dest, Patch, &block)
12
+ end
13
+
14
+ # Executes a request which uses a representation
15
+ # and returns its body.
16
+ def send_entity(path, data, initheader, dest, type, &block)
17
+ res = nil
18
+ request(type.new(path, initheader), data) {|r|
19
+ r.read_body dest, &block
20
+ res = r
21
+ }
22
+ unless @newimpl
23
+ res.value
24
+ return res, res.body
25
+ end
26
+ res
27
+ end
28
+ end
29
+
30
+ class Patch < HTTPRequest
31
+ METHOD = 'PATCH'
32
+ REQUEST_HAS_BODY = true
33
+ RESPONSE_HAS_BODY = true
34
+ end
35
+ end
36
+
37
+ #
38
+ # Replace the request method in Net::HTTP to sniff the body type
39
+ # and set the stream if appropriate
40
+ #
41
+ # Taken from:
42
+ # http://www.missiondata.com/blog/ruby/29/streaming-data-to-s3-with-ruby/
43
+
44
+ alias __request__ request
45
+
46
+ def request(req, body=nil, &block)
47
+ if body != nil && body.respond_to?(:read)
48
+ req.body_stream = body
49
+ return __request__(req, nil, &block)
50
+ else
51
+ return __request__(req, body, &block)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,235 @@
1
+ require 'tempfile'
2
+ require 'stringio'
3
+ require 'mime/types'
4
+
5
+ module RestClient
6
+ module Payload
7
+ extend self
8
+
9
+ def generate(params)
10
+ if params.is_a?(String)
11
+ Base.new(params)
12
+ elsif params.respond_to?(:read)
13
+ Streamed.new(params)
14
+ elsif params
15
+ if params.delete(:multipart) == true || has_file?(params)
16
+ Multipart.new(params)
17
+ else
18
+ UrlEncoded.new(params)
19
+ end
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ def has_file?(params)
26
+ params.any? do |_, v|
27
+ case v
28
+ when Hash
29
+ has_file?(v)
30
+ when Array
31
+ has_file_array?(v)
32
+ else
33
+ v.respond_to?(:path) && v.respond_to?(:read)
34
+ end
35
+ end
36
+ end
37
+
38
+ def has_file_array?(params)
39
+ params.any? do |v|
40
+ case v
41
+ when Hash
42
+ has_file?(v)
43
+ when Array
44
+ has_file_array?(v)
45
+ else
46
+ v.respond_to?(:path) && v.respond_to?(:read)
47
+ end
48
+ end
49
+ end
50
+
51
+ class Base
52
+ def initialize(params)
53
+ build_stream(params)
54
+ end
55
+
56
+ def build_stream(params)
57
+ @stream = StringIO.new(params)
58
+ @stream.seek(0)
59
+ end
60
+
61
+ def read(bytes=nil)
62
+ @stream.read(bytes)
63
+ end
64
+
65
+ alias :to_s :read
66
+
67
+ # Flatten parameters by converting hashes of hashes to flat hashes
68
+ # {keys1 => {keys2 => value}} will be transformed into [keys1[key2], value]
69
+ def flatten_params(params, parent_key = nil)
70
+ result = []
71
+ params.each do |key, value|
72
+ calculated_key = parent_key ? "#{parent_key}[#{handle_key(key)}]" : handle_key(key)
73
+ if value.is_a? Hash
74
+ result += flatten_params(value, calculated_key)
75
+ elsif value.is_a? Array
76
+ result += flatten_params_array(value, calculated_key)
77
+ else
78
+ result << [calculated_key, value]
79
+ end
80
+ end
81
+ result
82
+ end
83
+
84
+ def flatten_params_array value, calculated_key
85
+ result = []
86
+ value.each do |elem|
87
+ if elem.is_a? Hash
88
+ result += flatten_params(elem, calculated_key)
89
+ elsif elem.is_a? Array
90
+ result += flatten_params_array(elem, calculated_key)
91
+ else
92
+ result << ["#{calculated_key}[]", elem]
93
+ end
94
+ end
95
+ result
96
+ end
97
+
98
+ def headers
99
+ {'Content-Length' => size.to_s}
100
+ end
101
+
102
+ def size
103
+ @stream.size
104
+ end
105
+
106
+ alias :length :size
107
+
108
+ def close
109
+ @stream.close unless @stream.closed?
110
+ end
111
+
112
+ def inspect
113
+ result = to_s.inspect
114
+ @stream.seek(0)
115
+ result
116
+ end
117
+
118
+ def short_inspect
119
+ (size > 500 ? "#{size} byte(s) length" : inspect)
120
+ end
121
+
122
+ end
123
+
124
+ class Streamed < Base
125
+ def build_stream(params = nil)
126
+ @stream = params
127
+ end
128
+
129
+ def size
130
+ if @stream.respond_to?(:size)
131
+ @stream.size
132
+ elsif @stream.is_a?(IO)
133
+ @stream.stat.size
134
+ end
135
+ end
136
+
137
+ alias :length :size
138
+ end
139
+
140
+ class UrlEncoded < Base
141
+ def build_stream(params = nil)
142
+ @stream = StringIO.new(flatten_params(params).collect do |entry|
143
+ "#{entry[0]}=#{handle_key(entry[1])}"
144
+ end.join("&"))
145
+ @stream.seek(0)
146
+ end
147
+
148
+ # for UrlEncoded escape the keys
149
+ def handle_key key
150
+ URI.escape(key.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
151
+ end
152
+
153
+ def headers
154
+ super.merge({'Content-Type' => 'application/x-www-form-urlencoded'})
155
+ end
156
+ end
157
+
158
+ class Multipart < Base
159
+ EOL = "\r\n"
160
+
161
+ def build_stream(params)
162
+ b = "--#{boundary}"
163
+
164
+ @stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
165
+ @stream.binmode
166
+ @stream.write(b + EOL)
167
+
168
+ if params.is_a? Hash
169
+ x = flatten_params(params)
170
+ else
171
+ x = params
172
+ end
173
+
174
+ last_index = x.length - 1
175
+ x.each_with_index do |a, index|
176
+ k, v = * a
177
+ if v.respond_to?(:read) && v.respond_to?(:path)
178
+ create_file_field(@stream, k, v)
179
+ else
180
+ create_regular_field(@stream, k, v)
181
+ end
182
+ @stream.write(EOL + b)
183
+ @stream.write(EOL) unless last_index == index
184
+ end
185
+ @stream.write('--')
186
+ @stream.write(EOL)
187
+ @stream.seek(0)
188
+ end
189
+
190
+ def create_regular_field(s, k, v)
191
+ s.write("Content-Disposition: form-data; name=\"#{k}\"")
192
+ s.write(EOL)
193
+ s.write(EOL)
194
+ s.write(v)
195
+ end
196
+
197
+ def create_file_field(s, k, v)
198
+ begin
199
+ s.write("Content-Disposition: form-data;")
200
+ s.write(" name=\"#{k}\";") unless (k.nil? || k=='')
201
+ s.write(" filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
202
+ s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
203
+ s.write(EOL)
204
+ while data = v.read(8124)
205
+ s.write(data)
206
+ end
207
+ ensure
208
+ v.close if v.respond_to?(:close)
209
+ end
210
+ end
211
+
212
+ def mime_for(path)
213
+ mime = MIME::Types.type_for path
214
+ mime.empty? ? 'text/plain' : mime[0].content_type
215
+ end
216
+
217
+ def boundary
218
+ @boundary ||= rand(1_000_000).to_s
219
+ end
220
+
221
+ # for Multipart do not escape the keys
222
+ def handle_key key
223
+ key
224
+ end
225
+
226
+ def headers
227
+ super.merge({'Content-Type' => %Q{multipart/form-data; boundary=#{boundary}}})
228
+ end
229
+
230
+ def close
231
+ @stream.close!
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,34 @@
1
+ module RestClient
2
+ # The response from RestClient on a raw request looks like a string, but is
3
+ # actually one of these. 99% of the time you're making a rest call all you
4
+ # care about is the body, but on the occassion you want to fetch the
5
+ # headers you can:
6
+ #
7
+ # RestClient.get('http://example.com').headers[:content_type]
8
+ #
9
+ # In addition, if you do not use the response as a string, you can access
10
+ # a Tempfile object at res.file, which contains the path to the raw
11
+ # downloaded request body.
12
+ class RawResponse
13
+
14
+ include AbstractResponse
15
+
16
+ attr_reader :file
17
+
18
+ def initialize tempfile, net_http_res, args
19
+ @net_http_res = net_http_res
20
+ @args = args
21
+ @file = tempfile
22
+ end
23
+
24
+ def to_s
25
+ @file.open
26
+ @file.read
27
+ end
28
+
29
+ def size
30
+ File.size file
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,316 @@
1
+ require 'tempfile'
2
+ require 'mime/types'
3
+ require 'cgi'
4
+
5
+ module RestClient
6
+ # This class is used internally by RestClient to send the request, but you can also
7
+ # call it directly if you'd like to use a method not supported by the
8
+ # main API. For example:
9
+ #
10
+ # RestClient::Request.execute(:method => :head, :url => 'http://example.com')
11
+ #
12
+ # Mandatory parameters:
13
+ # * :method
14
+ # * :url
15
+ # Optional parameters (have a look at ssl and/or uri for some explanations):
16
+ # * :headers a hash containing the request headers
17
+ # * :cookies will replace possible cookies in the :headers
18
+ # * :user and :password for basic auth, will be replaced by a user/password available in the :url
19
+ # * :block_response call the provided block with the HTTPResponse as parameter
20
+ # * :raw_response return a low-level RawResponse instead of a Response
21
+ # * :max_redirects maximum number of redirections (default to 10)
22
+ # * :verify_ssl enable ssl verification, possible values are constants from OpenSSL::SSL
23
+ # * :timeout and :open_timeout passing in -1 will disable the timeout by setting the corresponding net timeout values to nil
24
+ # * :ssl_client_cert, :ssl_client_key, :ssl_ca_file
25
+ class Request
26
+
27
+ attr_reader :method, :url, :headers, :cookies,
28
+ :payload, :user, :password, :timeout, :max_redirects,
29
+ :open_timeout, :raw_response, :verify_ssl, :ssl_client_cert,
30
+ :ssl_client_key, :ssl_ca_file, :processed_headers, :args
31
+
32
+ def self.execute(args, & block)
33
+ new(args).execute(& block)
34
+ end
35
+
36
+ def initialize args
37
+ @method = args[:method] or raise ArgumentError, "must pass :method"
38
+ @headers = args[:headers] || {}
39
+ if args[:url]
40
+ @url = process_url_params(args[:url], headers)
41
+ else
42
+ raise ArgumentError, "must pass :url"
43
+ end
44
+ @cookies = @headers.delete(:cookies) || args[:cookies] || {}
45
+ @payload = Payload.generate(args[:payload])
46
+ @user = args[:user]
47
+ @password = args[:password]
48
+ @timeout = @headers[:timeout]
49
+ @open_timeout = @headers[:open_timeout]
50
+ @block_response = args[:block_response]
51
+ @raw_response = args[:raw_response] || false
52
+ @verify_ssl = args[:verify_ssl] || false
53
+ @ssl_client_cert = args[:ssl_client_cert] || nil
54
+ @ssl_client_key = args[:ssl_client_key] || nil
55
+ @ssl_ca_file = args[:ssl_ca_file] || nil
56
+ @tf = nil # If you are a raw request, this is your tempfile
57
+ @max_redirects = args[:max_redirects] || 10
58
+ @processed_headers = make_headers headers
59
+ @args = args
60
+ end
61
+
62
+ def execute & block
63
+ uri = parse_url_with_auth(url)
64
+ transmit uri, net_http_request_class(method).new(uri.request_uri, processed_headers), payload, & block
65
+ ensure
66
+ payload.close if payload
67
+ end
68
+
69
+ # Extract the query parameters and append them to the url
70
+ def process_url_params url, headers
71
+ url_params = {}
72
+ headers.delete_if do |key, value|
73
+ if 'params' == key.to_s.downcase && value.is_a?(Hash)
74
+ url_params.merge! value
75
+ true
76
+ else
77
+ false
78
+ end
79
+ end
80
+ unless url_params.empty?
81
+ query_string = url_params.collect { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
82
+ url + "?#{query_string}"
83
+ else
84
+ url
85
+ end
86
+ end
87
+
88
+ def make_headers user_headers
89
+ unless @cookies.empty?
90
+ user_headers[:cookie] = @cookies.map { |(key, val)| "#{key.to_s}=#{CGI::unescape(val)}" }.sort.join('; ')
91
+ end
92
+ headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
93
+ headers.merge!(@payload.headers) if @payload
94
+ headers
95
+ end
96
+
97
+ def net_http_class
98
+ if RestClient.proxy
99
+ proxy_uri = URI.parse(RestClient.proxy)
100
+ Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
101
+ else
102
+ Net::HTTP
103
+ end
104
+ end
105
+
106
+ def net_http_request_class(method)
107
+ Net::HTTP.const_get(method.to_s.capitalize)
108
+ end
109
+
110
+ def parse_url(url)
111
+ url = "http://#{url}" unless url.match(/^http/)
112
+ URI.parse(url)
113
+ end
114
+
115
+ def parse_url_with_auth(url)
116
+ uri = parse_url(url)
117
+ @user = CGI.unescape(uri.user) if uri.user
118
+ @password = CGI.unescape(uri.password) if uri.password
119
+ uri
120
+ end
121
+
122
+ def process_payload(p=nil, parent_key=nil)
123
+ unless p.is_a?(Hash)
124
+ p
125
+ else
126
+ @headers[:content_type] ||= 'application/x-www-form-urlencoded'
127
+ p.keys.map do |k|
128
+ key = parent_key ? "#{parent_key}[#{k}]" : k
129
+ if p[k].is_a? Hash
130
+ process_payload(p[k], key)
131
+ else
132
+ value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
133
+ "#{key}=#{value}"
134
+ end
135
+ end.join("&")
136
+ end
137
+ end
138
+
139
+ def transmit uri, req, payload, & block
140
+ setup_credentials req
141
+
142
+ net = net_http_class.new(uri.host, uri.port)
143
+ net.use_ssl = uri.is_a?(URI::HTTPS)
144
+ if (@verify_ssl == false) || (@verify_ssl == OpenSSL::SSL::VERIFY_NONE)
145
+ net.verify_mode = OpenSSL::SSL::VERIFY_NONE
146
+ elsif @verify_ssl.is_a? Integer
147
+ net.verify_mode = @verify_ssl
148
+ net.verify_callback = lambda do |preverify_ok, ssl_context|
149
+ if (!preverify_ok) || ssl_context.error != 0
150
+ err_msg = "SSL Verification failed -- Preverify: #{preverify_ok}, Error: #{ssl_context.error_string} (#{ssl_context.error})"
151
+ raise SSLCertificateNotVerified.new(err_msg)
152
+ end
153
+ true
154
+ end
155
+ end
156
+ net.cert = @ssl_client_cert if @ssl_client_cert
157
+ net.key = @ssl_client_key if @ssl_client_key
158
+ net.ca_file = @ssl_ca_file if @ssl_ca_file
159
+ net.read_timeout = @timeout if @timeout
160
+ net.open_timeout = @open_timeout if @open_timeout
161
+
162
+ # disable the timeout if the timeout value is -1
163
+ net.read_timeout = nil if @timeout == -1
164
+ net.out_timeout = nil if @open_timeout == -1
165
+
166
+ RestClient.before_execution_procs.each do |before_proc|
167
+ before_proc.call(req, args)
168
+ end
169
+
170
+ log_request
171
+
172
+ net.start do |http|
173
+ if @block_response
174
+ http.request(req, payload ? payload.to_s : nil, & @block_response)
175
+ else
176
+ res = http.request(req, payload ? payload.to_s : nil) { |http_response| fetch_body(http_response) }
177
+ log_response res
178
+ process_result res, & block
179
+ end
180
+ end
181
+ rescue EOFError
182
+ raise RestClient::ServerBrokeConnection
183
+ rescue Timeout::Error
184
+ raise RestClient::RequestTimeout
185
+ end
186
+
187
+ def setup_credentials(req)
188
+ req.basic_auth(user, password) if user
189
+ end
190
+
191
+ def fetch_body(http_response)
192
+ if @raw_response
193
+ # Taken from Chef, which as in turn...
194
+ # Stolen from http://www.ruby-forum.com/topic/166423
195
+ # Kudos to _why!
196
+ @tf = Tempfile.new("rest-client")
197
+ size, total = 0, http_response.header['Content-Length'].to_i
198
+ http_response.read_body do |chunk|
199
+ @tf.write chunk
200
+ size += chunk.size
201
+ if RestClient.log
202
+ if size == 0
203
+ RestClient.log << "#{@method} #{@url} done (0 length file\n)"
204
+ elsif total == 0
205
+ RestClient.log << "#{@method} #{@url} (zero content length)\n"
206
+ else
207
+ RestClient.log << "#{@method} #{@url} %d%% done (%d of %d)\n" % [(size * 100) / total, size, total]
208
+ end
209
+ end
210
+ end
211
+ @tf.close
212
+ @tf
213
+ else
214
+ http_response.read_body
215
+ end
216
+ http_response
217
+ end
218
+
219
+ def process_result res, & block
220
+ if @raw_response
221
+ # We don't decode raw requests
222
+ response = RawResponse.new(@tf, res, args)
223
+ else
224
+ response = Response.create(Request.decode(res['content-encoding'], res.body), res, args)
225
+ end
226
+
227
+ if block_given?
228
+ block.call(response, self, res, & block)
229
+ else
230
+ response.return!(self, res, & block)
231
+ end
232
+
233
+ end
234
+
235
+ def self.decode content_encoding, body
236
+ if (!body) || body.empty?
237
+ body
238
+ elsif content_encoding == 'gzip'
239
+ Zlib::GzipReader.new(StringIO.new(body)).read
240
+ elsif content_encoding == 'deflate'
241
+ begin
242
+ Zlib::Inflate.new.inflate body
243
+ rescue Zlib::DataError
244
+ # No luck with Zlib decompression. Let's try with raw deflate,
245
+ # like some broken web servers do.
246
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate body
247
+ end
248
+ else
249
+ body
250
+ end
251
+ end
252
+
253
+ def log_request
254
+ if RestClient.log
255
+ out = []
256
+ out << "RestClient.#{method} #{url.inspect}"
257
+ out << payload.short_inspect if payload
258
+ out << processed_headers.to_a.sort.map { |(k, v)| [k.inspect, v.inspect].join("=>") }.join(", ")
259
+ RestClient.log << out.join(', ') + "\n"
260
+ end
261
+ end
262
+
263
+ def log_response res
264
+ if RestClient.log
265
+ size = @raw_response ? File.size(@tf.path) : (res.body.nil? ? 0 : res.body.size)
266
+ RestClient.log << "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes\n"
267
+ end
268
+ end
269
+
270
+ # Return a hash of headers whose keys are capitalized strings
271
+ def stringify_headers headers
272
+ headers.inject({}) do |result, (key, value)|
273
+ if key.is_a? Symbol
274
+ key = key.to_s.split(/_/).map { |w| w.capitalize }.join('-')
275
+ end
276
+ if 'CONTENT-TYPE' == key.upcase
277
+ target_value = value.to_s
278
+ result[key] = MIME::Types.type_for_extension target_value
279
+ elsif 'ACCEPT' == key.upcase
280
+ # Accept can be composed of several comma-separated values
281
+ if value.is_a? Array
282
+ target_values = value
283
+ else
284
+ target_values = value.to_s.split ','
285
+ end
286
+ result[key] = target_values.map { |ext| MIME::Types.type_for_extension(ext.to_s.strip) }.join(', ')
287
+ else
288
+ result[key] = value.to_s
289
+ end
290
+ result
291
+ end
292
+ end
293
+
294
+ def default_headers
295
+ {:accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate'}
296
+ end
297
+
298
+ end
299
+ end
300
+
301
+ module MIME
302
+ class Types
303
+
304
+ # Return the first found content-type for a value considered as an extension or the value itself
305
+ def type_for_extension ext
306
+ candidates = @extension_index[ext]
307
+ candidates.empty? ? ext : candidates[0].content_type
308
+ end
309
+
310
+ class << self
311
+ def type_for_extension ext
312
+ @__types__.type_for_extension ext
313
+ end
314
+ end
315
+ end
316
+ end