rest-client 1.6.14

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.

Potentially problematic release.


This version of rest-client might be problematic. Click here for more details.

Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +3 -0
  5. data/AUTHORS +75 -0
  6. data/Gemfile +7 -0
  7. data/README.rdoc +300 -0
  8. data/Rakefile +49 -0
  9. data/bin/restclient +93 -0
  10. data/history.md +160 -0
  11. data/lib/rest-client.rb +2 -0
  12. data/lib/rest_client.rb +2 -0
  13. data/lib/restclient.rb +170 -0
  14. data/lib/restclient/abstract_response.rb +106 -0
  15. data/lib/restclient/exceptions.rb +198 -0
  16. data/lib/restclient/net_http_ext.rb +55 -0
  17. data/lib/restclient/payload.rb +240 -0
  18. data/lib/restclient/platform.rb +29 -0
  19. data/lib/restclient/raw_response.rb +34 -0
  20. data/lib/restclient/request.rb +360 -0
  21. data/lib/restclient/resource.rb +169 -0
  22. data/lib/restclient/response.rb +26 -0
  23. data/lib/restclient/version.rb +7 -0
  24. data/rest-client.gemspec +26 -0
  25. data/spec/abstract_response_spec.rb +85 -0
  26. data/spec/base.rb +13 -0
  27. data/spec/exceptions_spec.rb +98 -0
  28. data/spec/integration/capath_digicert/244b5494.0 +19 -0
  29. data/spec/integration/capath_digicert/81b9768f.0 +19 -0
  30. data/spec/integration/capath_digicert/README +8 -0
  31. data/spec/integration/capath_digicert/digicert.crt +19 -0
  32. data/spec/integration/certs/digicert.crt +19 -0
  33. data/spec/integration/certs/verisign.crt +14 -0
  34. data/spec/integration/request_spec.rb +75 -0
  35. data/spec/integration_spec.rb +38 -0
  36. data/spec/master_shake.jpg +0 -0
  37. data/spec/payload_spec.rb +244 -0
  38. data/spec/raw_response_spec.rb +17 -0
  39. data/spec/request2_spec.rb +35 -0
  40. data/spec/request_spec.rb +528 -0
  41. data/spec/resource_spec.rb +136 -0
  42. data/spec/response_spec.rb +169 -0
  43. data/spec/restclient_spec.rb +73 -0
  44. metadata +192 -0
@@ -0,0 +1,198 @@
1
+ module RestClient
2
+
3
+ STATUSES = {100 => 'Continue',
4
+ 101 => 'Switching Protocols',
5
+ 102 => 'Processing', #WebDAV
6
+
7
+ 200 => 'OK',
8
+ 201 => 'Created',
9
+ 202 => 'Accepted',
10
+ 203 => 'Non-Authoritative Information', # http/1.1
11
+ 204 => 'No Content',
12
+ 205 => 'Reset Content',
13
+ 206 => 'Partial Content',
14
+ 207 => 'Multi-Status', #WebDAV
15
+
16
+ 300 => 'Multiple Choices',
17
+ 301 => 'Moved Permanently',
18
+ 302 => 'Found',
19
+ 303 => 'See Other', # http/1.1
20
+ 304 => 'Not Modified',
21
+ 305 => 'Use Proxy', # http/1.1
22
+ 306 => 'Switch Proxy', # no longer used
23
+ 307 => 'Temporary Redirect', # http/1.1
24
+
25
+ 400 => 'Bad Request',
26
+ 401 => 'Unauthorized',
27
+ 402 => 'Payment Required',
28
+ 403 => 'Forbidden',
29
+ 404 => 'Resource Not Found',
30
+ 405 => 'Method Not Allowed',
31
+ 406 => 'Not Acceptable',
32
+ 407 => 'Proxy Authentication Required',
33
+ 408 => 'Request Timeout',
34
+ 409 => 'Conflict',
35
+ 410 => 'Gone',
36
+ 411 => 'Length Required',
37
+ 412 => 'Precondition Failed',
38
+ 413 => 'Request Entity Too Large',
39
+ 414 => 'Request-URI Too Long',
40
+ 415 => 'Unsupported Media Type',
41
+ 416 => 'Requested Range Not Satisfiable',
42
+ 417 => 'Expectation Failed',
43
+ 418 => 'I\'m A Teapot',
44
+ 421 => 'Too Many Connections From This IP',
45
+ 422 => 'Unprocessable Entity', #WebDAV
46
+ 423 => 'Locked', #WebDAV
47
+ 424 => 'Failed Dependency', #WebDAV
48
+ 425 => 'Unordered Collection', #WebDAV
49
+ 426 => 'Upgrade Required',
50
+ 449 => 'Retry With', #Microsoft
51
+ 450 => 'Blocked By Windows Parental Controls', #Microsoft
52
+
53
+ 500 => 'Internal Server Error',
54
+ 501 => 'Not Implemented',
55
+ 502 => 'Bad Gateway',
56
+ 503 => 'Service Unavailable',
57
+ 504 => 'Gateway Timeout',
58
+ 505 => 'HTTP Version Not Supported',
59
+ 506 => 'Variant Also Negotiates',
60
+ 507 => 'Insufficient Storage', #WebDAV
61
+ 509 => 'Bandwidth Limit Exceeded', #Apache
62
+ 510 => 'Not Extended'}
63
+
64
+ # Compatibility : make the Response act like a Net::HTTPResponse when needed
65
+ module ResponseForException
66
+ def method_missing symbol, *args
67
+ if net_http_res.respond_to? symbol
68
+ warn "[warning] The response contained in an RestClient::Exception is now a RestClient::Response instead of a Net::HTTPResponse, please update your code"
69
+ net_http_res.send symbol, *args
70
+ else
71
+ super
72
+ end
73
+ end
74
+ end
75
+
76
+ # This is the base RestClient exception class. Rescue it if you want to
77
+ # catch any exception that your request might raise
78
+ # You can get the status code by e.http_code, or see anything about the
79
+ # response via e.response.
80
+ # For example, the entire result body (which is
81
+ # probably an HTML error page) is e.response.
82
+ class Exception < RuntimeError
83
+ attr_accessor :response
84
+ attr_writer :message
85
+
86
+ def initialize response = nil, initial_response_code = nil
87
+ @response = response
88
+ @message = nil
89
+ @initial_response_code = initial_response_code
90
+
91
+ # compatibility: this make the exception behave like a Net::HTTPResponse
92
+ response.extend ResponseForException if response
93
+ end
94
+
95
+ def http_code
96
+ # return integer for compatibility
97
+ if @response
98
+ @response.code.to_i
99
+ else
100
+ @initial_response_code
101
+ end
102
+ end
103
+
104
+ def http_body
105
+ @response.body if @response
106
+ end
107
+
108
+ def inspect
109
+ "#{message}: #{http_body}"
110
+ end
111
+
112
+ def to_s
113
+ inspect
114
+ end
115
+
116
+ def message
117
+ @message || self.class.name
118
+ end
119
+
120
+ end
121
+
122
+ # Compatibility
123
+ class ExceptionWithResponse < Exception
124
+ end
125
+
126
+ # The request failed with an error code not managed by the code
127
+ class RequestFailed < ExceptionWithResponse
128
+
129
+ def message
130
+ "HTTP status code #{http_code}"
131
+ end
132
+
133
+ def to_s
134
+ message
135
+ end
136
+ end
137
+
138
+ # We will a create an exception for each status code, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
139
+ module Exceptions
140
+ # Map http status codes to the corresponding exception class
141
+ EXCEPTIONS_MAP = {}
142
+ end
143
+
144
+ STATUSES.each_pair do |code, message|
145
+
146
+ # Compatibility
147
+ superclass = ([304, 401, 404].include? code) ? ExceptionWithResponse : RequestFailed
148
+ klass = Class.new(superclass) do
149
+ send(:define_method, :message) {"#{http_code ? "#{http_code} " : ''}#{message}"}
150
+ end
151
+ klass_constant = const_set message.delete(' \-\''), klass
152
+ Exceptions::EXCEPTIONS_MAP[code] = klass_constant
153
+ end
154
+
155
+ # A redirect was encountered; caught by execute to retry with the new url.
156
+ class Redirect < Exception
157
+
158
+ def message
159
+ 'Redirect'
160
+ end
161
+
162
+ attr_accessor :url
163
+
164
+ def initialize(url)
165
+ @url = url
166
+ end
167
+ end
168
+
169
+ class MaxRedirectsReached < Exception
170
+ def message
171
+ 'Maximum number of redirect reached'
172
+ end
173
+ end
174
+
175
+ # The server broke the connection prior to the request completing. Usually
176
+ # this means it crashed, or sometimes that your network connection was
177
+ # severed before it could complete.
178
+ class ServerBrokeConnection < Exception
179
+ def initialize(message = 'Server broke connection')
180
+ super nil, nil
181
+ self.message = message
182
+ end
183
+ end
184
+
185
+ class SSLCertificateNotVerified < Exception
186
+ def initialize(message)
187
+ super nil, nil
188
+ self.message = message
189
+ end
190
+ end
191
+ end
192
+
193
+ # backwards compatibility
194
+ class RestClient::Request
195
+ Redirect = RestClient::Redirect
196
+ Unauthorized = RestClient::Unauthorized
197
+ RequestFailed = RestClient::RequestFailed
198
+ end
@@ -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,240 @@
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.is_a?(Hash)
13
+ if params.delete(:multipart) == true || has_file?(params)
14
+ Multipart.new(params)
15
+ else
16
+ UrlEncoded.new(params)
17
+ end
18
+ elsif params.respond_to?(:read)
19
+ Streamed.new(params)
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
+ parser.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
+
157
+ private
158
+ def parser
159
+ URI.const_defined?(:Parser) ? URI::Parser.new : URI
160
+ end
161
+ end
162
+
163
+ class Multipart < Base
164
+ EOL = "\r\n"
165
+
166
+ def build_stream(params)
167
+ b = "--#{boundary}"
168
+
169
+ @stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
170
+ @stream.binmode
171
+ @stream.write(b + EOL)
172
+
173
+ if params.is_a? Hash
174
+ x = flatten_params(params)
175
+ else
176
+ x = params
177
+ end
178
+
179
+ last_index = x.length - 1
180
+ x.each_with_index do |a, index|
181
+ k, v = * a
182
+ if v.respond_to?(:read) && v.respond_to?(:path)
183
+ create_file_field(@stream, k, v)
184
+ else
185
+ create_regular_field(@stream, k, v)
186
+ end
187
+ @stream.write(EOL + b)
188
+ @stream.write(EOL) unless last_index == index
189
+ end
190
+ @stream.write('--')
191
+ @stream.write(EOL)
192
+ @stream.seek(0)
193
+ end
194
+
195
+ def create_regular_field(s, k, v)
196
+ s.write("Content-Disposition: form-data; name=\"#{k}\"")
197
+ s.write(EOL)
198
+ s.write(EOL)
199
+ s.write(v)
200
+ end
201
+
202
+ def create_file_field(s, k, v)
203
+ begin
204
+ s.write("Content-Disposition: form-data;")
205
+ s.write(" name=\"#{k}\";") unless (k.nil? || k=='')
206
+ s.write(" filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
207
+ s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
208
+ s.write(EOL)
209
+ while data = v.read(8124)
210
+ s.write(data)
211
+ end
212
+ ensure
213
+ v.close if v.respond_to?(:close)
214
+ end
215
+ end
216
+
217
+ def mime_for(path)
218
+ mime = MIME::Types.type_for path
219
+ mime.empty? ? 'text/plain' : mime[0].content_type
220
+ end
221
+
222
+ def boundary
223
+ @boundary ||= rand(1_000_000).to_s
224
+ end
225
+
226
+ # for Multipart do not escape the keys
227
+ def handle_key key
228
+ key
229
+ end
230
+
231
+ def headers
232
+ super.merge({'Content-Type' => %Q{multipart/form-data; boundary=#{boundary}}})
233
+ end
234
+
235
+ def close
236
+ @stream.close!
237
+ end
238
+ end
239
+ end
240
+ end