rest-client 0.8.2 → 0.9

Sign up to get free protection for your applications and to get access to all the features.

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