rest-client 0.8.2 → 0.9

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.

data/Rakefile CHANGED
@@ -31,7 +31,7 @@ require 'rake/gempackagetask'
31
31
  require 'rake/rdoctask'
32
32
  require 'fileutils'
33
33
 
34
- version = "0.8.2"
34
+ version = "0.9"
35
35
  name = "rest-client"
36
36
 
37
37
  spec = Gem::Specification.new do |s|
@@ -76,8 +76,9 @@ Rake::RDocTask.new do |t|
76
76
  t.title = "rest-client, fetch RESTful resources effortlessly"
77
77
  t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
78
78
  t.options << '--charset' << 'utf-8'
79
- t.rdoc_files.include('README')
80
- t.rdoc_files.include('lib/*.rb')
79
+ t.rdoc_files.include('README.rdoc')
80
+ t.rdoc_files.include('lib/restclient.rb')
81
+ t.rdoc_files.include('lib/restclient/*.rb')
81
82
  end
82
83
 
83
84
  CLEAN.include [ 'pkg', '*.gem', '.config' ]
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  $:.unshift File.dirname(__FILE__) + "/../lib"
4
- require 'rest_client'
4
+ require 'restclient'
5
5
 
6
6
  require "yaml"
7
7
 
@@ -1,250 +1,2 @@
1
- require 'uri'
2
- require 'net/https'
3
- require 'zlib'
4
- require 'stringio'
5
-
6
- require File.dirname(__FILE__) + '/resource'
7
- require File.dirname(__FILE__) + '/request_errors'
8
-
9
- # This module's static methods are the entry point for using the REST client.
10
- #
11
- # # GET
12
- # xml = RestClient.get 'http://example.com/resource'
13
- # jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
14
- #
15
- # # authentication and SSL
16
- # RestClient.get 'https://user:password@example.com/private/resource'
17
- #
18
- # # POST or PUT with a hash sends parameters as a urlencoded form body
19
- # RestClient.post 'http://example.com/resource', :param1 => 'one'
20
- #
21
- # # nest hash parameters
22
- # RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
23
- #
24
- # # POST and PUT with raw payloads
25
- # RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
26
- # RestClient.post 'http://example.com/resource.xml', xml_doc
27
- # RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
28
- #
29
- # # DELETE
30
- # RestClient.delete 'http://example.com/resource'
31
- #
32
- # To use with a proxy, just set RestClient.proxy to the proper http proxy:
33
- #
34
- # RestClient.proxy = "http://proxy.example.com/"
35
- #
36
- # Or inherit the proxy from the environment:
37
- #
38
- # RestClient.proxy = ENV['http_proxy']
39
- #
40
- # For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call:
41
- #
42
- # >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz'
43
- # => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
44
- #
45
- module RestClient
46
- def self.get(url, headers={})
47
- Request.execute(:method => :get,
48
- :url => url,
49
- :headers => headers)
50
- end
51
-
52
- def self.post(url, payload, headers={})
53
- Request.execute(:method => :post,
54
- :url => url,
55
- :payload => payload,
56
- :headers => headers)
57
- end
58
-
59
- def self.put(url, payload, headers={})
60
- Request.execute(:method => :put,
61
- :url => url,
62
- :payload => payload,
63
- :headers => headers)
64
- end
65
-
66
- def self.delete(url, headers={})
67
- Request.execute(:method => :delete,
68
- :url => url,
69
- :headers => headers)
70
- end
71
-
72
- class <<self
73
- attr_accessor :proxy
74
- end
75
-
76
- # Print log of RestClient calls. Value can be stdout, stderr, or a filename.
77
- # You can also configure logging by the environment variable RESTCLIENT_LOG.
78
- def self.log=(log)
79
- @@log = log
80
- end
81
-
82
- def self.log # :nodoc:
83
- return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
84
- return @@log if defined? @@log
85
- nil
86
- end
87
-
88
- # Internal class used to build and execute the request.
89
- class Request
90
- attr_reader :method, :url, :payload, :headers, :user, :password
91
-
92
- def self.execute(args)
93
- new(args).execute
94
- end
95
-
96
- def initialize(args)
97
- @method = args[:method] or raise ArgumentError, "must pass :method"
98
- @url = args[:url] or raise ArgumentError, "must pass :url"
99
- @headers = args[:headers] || {}
100
- @payload = process_payload(args[:payload])
101
- @user = args[:user]
102
- @password = args[:password]
103
- end
104
-
105
- def execute
106
- execute_inner
107
- rescue Redirect => e
108
- @url = e.url
109
- execute
110
- end
111
-
112
- def execute_inner
113
- uri = parse_url_with_auth(url)
114
- transmit uri, net_http_request_class(method).new(uri.request_uri, make_headers(headers)), payload
115
- end
116
-
117
- def make_headers(user_headers)
118
- default_headers.merge(user_headers).inject({}) do |final, (key, value)|
119
- final[key.to_s.gsub(/_/, '-').capitalize] = value.to_s
120
- final
121
- end
122
- end
123
-
124
- def net_http_class
125
- if RestClient.proxy
126
- proxy_uri = URI.parse(RestClient.proxy)
127
- Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
128
- else
129
- Net::HTTP
130
- end
131
- end
132
-
133
- def net_http_request_class(method)
134
- Net::HTTP.const_get(method.to_s.capitalize)
135
- end
136
-
137
- def parse_url(url)
138
- url = "http://#{url}" unless url.match(/^http/)
139
- URI.parse(url)
140
- end
141
-
142
- def parse_url_with_auth(url)
143
- uri = parse_url(url)
144
- @user = uri.user if uri.user
145
- @password = uri.password if uri.password
146
- uri
147
- end
148
-
149
- def process_payload(p=nil, parent_key=nil)
150
- unless p.is_a?(Hash)
151
- p
152
- else
153
- @headers[:content_type] ||= 'application/x-www-form-urlencoded'
154
- p.keys.map do |k|
155
- key = parent_key ? "#{parent_key}[#{k}]" : k
156
- if p[k].is_a? Hash
157
- process_payload(p[k], key)
158
- else
159
- value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
160
- "#{key}=#{value}"
161
- end
162
- end.join("&")
163
- end
164
- end
165
-
166
- def transmit(uri, req, payload)
167
- setup_credentials(req)
168
-
169
- net = net_http_class.new(uri.host, uri.port)
170
- net.use_ssl = uri.is_a?(URI::HTTPS)
171
- net.verify_mode = OpenSSL::SSL::VERIFY_NONE
172
-
173
- display_log request_log
174
-
175
- net.start do |http|
176
- res = http.request(req, payload || "")
177
- display_log response_log(res)
178
- process_result res
179
- end
180
- rescue EOFError
181
- raise RestClient::ServerBrokeConnection
182
- rescue Timeout::Error
183
- raise RestClient::RequestTimeout
184
- end
185
-
186
- def setup_credentials(req)
187
- req.basic_auth(user, password) if user
188
- end
189
-
190
- def process_result(res)
191
- if %w(200 201 202).include? res.code
192
- decode res['content-encoding'], res.body
193
- elsif %w(301 302 303).include? res.code
194
- url = res.header['Location']
195
-
196
- if url !~ /^http/
197
- uri = URI.parse(@url)
198
- uri.path = "/#{url}".squeeze('/')
199
- url = uri.to_s
200
- end
201
-
202
- raise Redirect, url
203
- elsif res.code == "401"
204
- raise Unauthorized, res
205
- elsif res.code == "404"
206
- raise ResourceNotFound, res
207
- else
208
- raise RequestFailed, res
209
- end
210
- end
211
-
212
- def decode(content_encoding, body)
213
- if content_encoding == 'gzip'
214
- Zlib::GzipReader.new(StringIO.new(body)).read
215
- elsif content_encoding == 'deflate'
216
- Zlib::Inflate.new.inflate(body)
217
- else
218
- body
219
- end
220
- end
221
-
222
- def request_log
223
- out = []
224
- out << "RestClient.#{method} #{url.inspect}"
225
- out << (payload.size > 100 ? "(#{payload.size} byte payload)".inspect : payload.inspect) if payload
226
- out << headers.inspect.gsub(/^\{/, '').gsub(/\}$/, '') unless headers.empty?
227
- out.join(', ')
228
- end
229
-
230
- def response_log(res)
231
- "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{res.body.size} bytes"
232
- end
233
-
234
- def display_log(msg)
235
- return unless log_to = RestClient.log
236
-
237
- if log_to == 'stdout'
238
- STDOUT.puts msg
239
- elsif log_to == 'stderr'
240
- STDERR.puts msg
241
- else
242
- File.open(log_to, 'a') { |f| f.puts msg }
243
- end
244
- end
245
-
246
- def default_headers
247
- { :accept => 'application/xml', :accept_encoding => 'gzip, deflate' }
248
- end
249
- end
250
- end
1
+ # This file exists for backward compatbility with require 'rest_client'
2
+ require File.dirname(__FILE__) + '/restclient'
@@ -0,0 +1,83 @@
1
+ require 'uri'
2
+ require 'net/https'
3
+ require 'zlib'
4
+ require 'stringio'
5
+
6
+ require File.dirname(__FILE__) + '/restclient/request'
7
+ require File.dirname(__FILE__) + '/restclient/response'
8
+ require File.dirname(__FILE__) + '/restclient/resource'
9
+ require File.dirname(__FILE__) + '/restclient/exceptions'
10
+
11
+ # This module's static methods are the entry point for using the REST client.
12
+ #
13
+ # # GET
14
+ # xml = RestClient.get 'http://example.com/resource'
15
+ # jpg = RestClient.get 'http://example.com/resource', :accept => 'image/jpg'
16
+ #
17
+ # # authentication and SSL
18
+ # RestClient.get 'https://user:password@example.com/private/resource'
19
+ #
20
+ # # POST or PUT with a hash sends parameters as a urlencoded form body
21
+ # RestClient.post 'http://example.com/resource', :param1 => 'one'
22
+ #
23
+ # # nest hash parameters
24
+ # RestClient.post 'http://example.com/resource', :nested => { :param1 => 'one' }
25
+ #
26
+ # # POST and PUT with raw payloads
27
+ # RestClient.post 'http://example.com/resource', 'the post body', :content_type => 'text/plain'
28
+ # RestClient.post 'http://example.com/resource.xml', xml_doc
29
+ # RestClient.put 'http://example.com/resource.pdf', File.read('my.pdf'), :content_type => 'application/pdf'
30
+ #
31
+ # # DELETE
32
+ # RestClient.delete 'http://example.com/resource'
33
+ #
34
+ # # retreive the response headers
35
+ # res = RestClient.get 'http://example.com/some.jpg'
36
+ # res.headers[:content_type] # => 'image/jpg'
37
+ #
38
+ # To use with a proxy, just set RestClient.proxy to the proper http proxy:
39
+ #
40
+ # RestClient.proxy = "http://proxy.example.com/"
41
+ #
42
+ # Or inherit the proxy from the environment:
43
+ #
44
+ # RestClient.proxy = ENV['http_proxy']
45
+ #
46
+ # For live tests of RestClient, try using http://rest-test.heroku.com, which echoes back information about the rest call:
47
+ #
48
+ # >> RestClient.put 'http://rest-test.heroku.com/resource', :foo => 'baz'
49
+ # => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
50
+ #
51
+ module RestClient
52
+ def self.get(url, headers={})
53
+ Request.execute(:method => :get, :url => url, :headers => headers)
54
+ end
55
+
56
+ def self.post(url, payload, headers={})
57
+ Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
58
+ end
59
+
60
+ def self.put(url, payload, headers={})
61
+ Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
62
+ end
63
+
64
+ def self.delete(url, headers={})
65
+ Request.execute(:method => :delete, :url => url, :headers => headers)
66
+ end
67
+
68
+ class << self
69
+ attr_accessor :proxy
70
+ end
71
+
72
+ # Print log of RestClient calls. Value can be stdout, stderr, or a filename.
73
+ # You can also configure logging by the environment variable RESTCLIENT_LOG.
74
+ def self.log=(log)
75
+ @@log = log
76
+ end
77
+
78
+ def self.log # :nodoc:
79
+ return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
80
+ return @@log if defined? @@log
81
+ nil
82
+ end
83
+ end
@@ -30,6 +30,10 @@ module RestClient
30
30
  end
31
31
  end
32
32
 
33
+ class NotModified < Exception
34
+ ErrorMessage = 'NotModified'
35
+ end
36
+
33
37
  # Authorization is required to access the resource specified.
34
38
  class Unauthorized < ExceptionWithResponse
35
39
  ErrorMessage = 'Unauthorized'
@@ -40,7 +44,9 @@ module RestClient
40
44
  ErrorMessage = 'Resource not found'
41
45
  end
42
46
 
43
- # The server broke the connection prior to the request completing.
47
+ # The server broke the connection prior to the request completing. Usually
48
+ # this means it crashed, or sometimes that your network connection was
49
+ # severed before it could complete.
44
50
  class ServerBrokeConnection < Exception
45
51
  ErrorMessage = 'Server broke connection'
46
52
  end
@@ -0,0 +1,178 @@
1
+ module RestClient
2
+ # This class is used internally by RestClient to send the request, but you can also
3
+ # access it internally if you'd like to use a method not directly supported by the
4
+ # main API. For example:
5
+ #
6
+ # RestClient::Request.execute(:method => :head, :url => 'http://example.com')
7
+ #
8
+ class Request
9
+ attr_reader :method, :url, :payload, :headers, :user, :password, :timeout
10
+
11
+ def self.execute(args)
12
+ new(args).execute
13
+ end
14
+
15
+ def initialize(args)
16
+ @method = args[:method] or raise ArgumentError, "must pass :method"
17
+ @url = args[:url] or raise ArgumentError, "must pass :url"
18
+ @headers = args[:headers] || {}
19
+ @payload = process_payload(args[:payload])
20
+ @user = args[:user]
21
+ @password = args[:password]
22
+ @timeout = args[:timeout]
23
+ end
24
+
25
+ def execute
26
+ execute_inner
27
+ rescue Redirect => e
28
+ @url = e.url
29
+ execute
30
+ end
31
+
32
+ def execute_inner
33
+ uri = parse_url_with_auth(url)
34
+ transmit uri, net_http_request_class(method).new(uri.request_uri, make_headers(headers)), payload
35
+ end
36
+
37
+ def make_headers(user_headers)
38
+ default_headers.merge(user_headers).inject({}) do |final, (key, value)|
39
+ final[key.to_s.gsub(/_/, '-').capitalize] = value.to_s
40
+ final
41
+ end
42
+ end
43
+
44
+ def net_http_class
45
+ if RestClient.proxy
46
+ proxy_uri = URI.parse(RestClient.proxy)
47
+ Net::HTTP::Proxy(proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
48
+ else
49
+ Net::HTTP
50
+ end
51
+ end
52
+
53
+ def net_http_request_class(method)
54
+ Net::HTTP.const_get(method.to_s.capitalize)
55
+ end
56
+
57
+ def parse_url(url)
58
+ url = "http://#{url}" unless url.match(/^http/)
59
+ URI.parse(url)
60
+ end
61
+
62
+ def parse_url_with_auth(url)
63
+ uri = parse_url(url)
64
+ @user = uri.user if uri.user
65
+ @password = uri.password if uri.password
66
+ uri
67
+ end
68
+
69
+ def process_payload(p=nil, parent_key=nil)
70
+ unless p.is_a?(Hash)
71
+ p
72
+ else
73
+ @headers[:content_type] ||= 'application/x-www-form-urlencoded'
74
+ p.keys.map do |k|
75
+ key = parent_key ? "#{parent_key}[#{k}]" : k
76
+ if p[k].is_a? Hash
77
+ process_payload(p[k], key)
78
+ else
79
+ value = URI.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
80
+ "#{key}=#{value}"
81
+ end
82
+ end.join("&")
83
+ end
84
+ end
85
+
86
+ def transmit(uri, req, payload)
87
+ setup_credentials(req)
88
+
89
+ net = net_http_class.new(uri.host, uri.port)
90
+ net.use_ssl = uri.is_a?(URI::HTTPS)
91
+ net.verify_mode = OpenSSL::SSL::VERIFY_NONE
92
+
93
+ display_log request_log
94
+
95
+ net.start do |http|
96
+ http.read_timeout = @timeout if @timeout
97
+ res = http.request(req, payload)
98
+ display_log response_log(res)
99
+ string = process_result(res)
100
+ if string
101
+ Response.new(string, res)
102
+ else
103
+ nil
104
+ end
105
+ end
106
+ rescue EOFError
107
+ raise RestClient::ServerBrokeConnection
108
+ rescue Timeout::Error
109
+ raise RestClient::RequestTimeout
110
+ end
111
+
112
+ def setup_credentials(req)
113
+ req.basic_auth(user, password) if user
114
+ end
115
+
116
+ def process_result(res)
117
+ if %w(200 201 202).include? res.code
118
+ decode res['content-encoding'], res.body
119
+ elsif %w(301 302 303).include? res.code
120
+ url = res.header['Location']
121
+
122
+ if url !~ /^http/
123
+ uri = URI.parse(@url)
124
+ uri.path = "/#{url}".squeeze('/')
125
+ url = uri.to_s
126
+ end
127
+
128
+ raise Redirect, url
129
+ elsif res.code == "304"
130
+ raise NotModified
131
+ elsif res.code == "401"
132
+ raise Unauthorized, res
133
+ elsif res.code == "404"
134
+ raise ResourceNotFound, res
135
+ else
136
+ raise RequestFailed, res
137
+ end
138
+ end
139
+
140
+ def decode(content_encoding, body)
141
+ if content_encoding == 'gzip'
142
+ Zlib::GzipReader.new(StringIO.new(body)).read
143
+ elsif content_encoding == 'deflate'
144
+ Zlib::Inflate.new.inflate(body)
145
+ else
146
+ body
147
+ end
148
+ end
149
+
150
+ def request_log
151
+ out = []
152
+ out << "RestClient.#{method} #{url.inspect}"
153
+ out << (payload.size > 100 ? "(#{payload.size} byte payload)".inspect : payload.inspect) if payload
154
+ out << headers.inspect.gsub(/^\{/, '').gsub(/\}$/, '') unless headers.empty?
155
+ out.join(', ')
156
+ end
157
+
158
+ def response_log(res)
159
+ "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{(res.body) ? res.body.size : nil} bytes"
160
+ end
161
+
162
+ def display_log(msg)
163
+ return unless log_to = RestClient.log
164
+
165
+ if log_to == 'stdout'
166
+ STDOUT.puts msg
167
+ elsif log_to == 'stderr'
168
+ STDERR.puts msg
169
+ else
170
+ File.open(log_to, 'a') { |f| f.puts msg }
171
+ end
172
+ end
173
+
174
+ def default_headers
175
+ { :accept => 'application/xml', :accept_encoding => 'gzip, deflate' }
176
+ end
177
+ end
178
+ end