rest-client 1.1.0 → 1.2.0

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.

@@ -8,10 +8,13 @@ of specifying actions: get, put, post, delete.
8
8
  require 'rest_client'
9
9
 
10
10
  RestClient.get 'http://example.com/resource'
11
+
11
12
  RestClient.get 'https://user:password@example.com/private/resource'
12
13
 
13
14
  RestClient.post 'http://example.com/resource', :param1 => 'one', :nested => { :param2 => 'two' }
14
15
 
16
+ RestClient.post "http://example.com/resource", { 'x' => 1 }.to_json, :content_type => :json, :accept => :json
17
+
15
18
  RestClient.delete 'http://example.com/resource'
16
19
 
17
20
  == Multipart
data/Rakefile CHANGED
@@ -7,8 +7,8 @@ Jeweler::Tasks.new do |s|
7
7
  s.description = "A simple REST client for Ruby, inspired by the Sinatra microframework style of specifying actions: get, put, post, delete."
8
8
  s.summary = "Simple REST client for Ruby, inspired by microframework syntax for specifying actions."
9
9
  s.author = "Adam Wiggins"
10
- s.email = "adam@heroku.com"
11
- s.homepage = "http://rest-client.heroku.com/"
10
+ s.email = "rest.client@librelist.com"
11
+ s.homepage = "http://github.com/archiloque/rest-client"
12
12
  s.rubyforge_project = "rest-client"
13
13
  s.has_rdoc = true
14
14
  s.files = FileList["[A-Z]*", "{bin,lib,spec}/**/*"]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.2.0
@@ -3,10 +3,10 @@ require 'zlib'
3
3
  require 'stringio'
4
4
 
5
5
  begin
6
- require 'net/https'
6
+ require 'net/https'
7
7
  rescue LoadError => e
8
- raise e unless RUBY_PLATFORM =~ /linux/
9
- raise LoadError, "no such file to load -- net/https. Try running apt-get install libopenssl-ruby"
8
+ raise e unless RUBY_PLATFORM =~ /linux/
9
+ raise LoadError, "no such file to load -- net/https. Try running apt-get install libopenssl-ruby"
10
10
  end
11
11
 
12
12
  require File.dirname(__FILE__) + '/restclient/request'
@@ -63,41 +63,42 @@ require File.dirname(__FILE__) + '/restclient/net_http_ext'
63
63
  # => "PUT http://rest-test.heroku.com/resource with a 7 byte payload, content type application/x-www-form-urlencoded {\"foo\"=>\"baz\"}"
64
64
  #
65
65
  module RestClient
66
- def self.get(url, headers={})
67
- Request.execute(:method => :get, :url => url, :headers => headers)
68
- end
69
66
 
70
- def self.post(url, payload, headers={})
71
- Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
72
- end
67
+ def self.get(url, headers={})
68
+ Request.execute(:method => :get, :url => url, :headers => headers)
69
+ end
73
70
 
74
- def self.put(url, payload, headers={})
75
- Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
76
- end
71
+ def self.post(url, payload, headers={})
72
+ Request.execute(:method => :post, :url => url, :payload => payload, :headers => headers)
73
+ end
77
74
 
78
- def self.delete(url, headers={})
79
- Request.execute(:method => :delete, :url => url, :headers => headers)
80
- end
75
+ def self.put(url, payload, headers={})
76
+ Request.execute(:method => :put, :url => url, :payload => payload, :headers => headers)
77
+ end
81
78
 
82
- def self.head(url, headers={})
83
- Request.execute(:method => :head, :url => url, :headers => headers)
84
- end
79
+ def self.delete(url, headers={})
80
+ Request.execute(:method => :delete, :url => url, :headers => headers)
81
+ end
85
82
 
86
- class << self
87
- attr_accessor :proxy
88
- end
83
+ def self.head(url, headers={})
84
+ Request.execute(:method => :head, :url => url, :headers => headers)
85
+ end
89
86
 
90
- # Print log of RestClient calls. Value can be stdout, stderr, or a filename.
91
- # You can also configure logging by the environment variable RESTCLIENT_LOG.
92
- def self.log=(log)
93
- @@log = log
94
- end
87
+ class << self
88
+ attr_accessor :proxy
89
+ end
90
+
91
+ # Print log of RestClient calls. Value can be stdout, stderr, or a filename.
92
+ # You can also configure logging by the environment variable RESTCLIENT_LOG.
93
+ def self.log=(log)
94
+ @@log = log
95
+ end
95
96
 
96
- def self.log # :nodoc:
97
- return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
98
- return @@log if defined? @@log
99
- nil
100
- end
97
+ def self.log # :nodoc:
98
+ return ENV['RESTCLIENT_LOG'] if ENV['RESTCLIENT_LOG']
99
+ return @@log if defined? @@log
100
+ nil
101
+ end
101
102
 
102
103
  def self.version
103
104
  version_path = File.dirname(__FILE__) + "/../VERSION"
@@ -1,88 +1,89 @@
1
1
  module RestClient
2
- # This is the base RestClient exception class. Rescue it if you want to
3
- # catch any exception that your request might raise
4
- class Exception < RuntimeError
5
- def message(default=nil)
6
- self.class::ErrorMessage
7
- end
8
- end
2
+ # This is the base RestClient exception class. Rescue it if you want to
3
+ # catch any exception that your request might raise
4
+ class Exception < RuntimeError
5
+ def message(default=nil)
6
+ self.class::ErrorMessage
7
+ end
8
+ end
9
9
 
10
- # Base RestClient exception when there's a response available
11
- class ExceptionWithResponse < Exception
12
- attr_accessor :response
10
+ # Base RestClient exception when there's a response available
11
+ class ExceptionWithResponse < Exception
12
+ attr_accessor :response
13
13
 
14
- def initialize(response=nil)
15
- @response = response
16
- end
14
+ def initialize(response=nil)
15
+ @response = response
16
+ end
17
17
 
18
- def http_code
19
- @response.code.to_i if @response
20
- end
18
+ def http_code
19
+ @response.code.to_i if @response
20
+ end
21
21
 
22
- def http_body
23
- RestClient::Request.decode(@response['content-encoding'], @response.body) if @response
24
- end
25
- end
22
+ def http_body
23
+ RestClient::Request.decode(@response['content-encoding'], @response.body) if @response
24
+ end
25
+ end
26
26
 
27
- # A redirect was encountered; caught by execute to retry with the new url.
28
- class Redirect < Exception
29
- ErrorMessage = "Redirect"
27
+ # A redirect was encountered; caught by execute to retry with the new url.
28
+ class Redirect < Exception
29
+ ErrorMessage = "Redirect"
30
30
 
31
- attr_accessor :url
32
- def initialize(url)
33
- @url = url
34
- end
35
- end
31
+ attr_accessor :url
36
32
 
37
- class NotModified < ExceptionWithResponse
38
- ErrorMessage = 'NotModified'
39
- end
33
+ def initialize(url)
34
+ @url = url
35
+ end
36
+ end
40
37
 
41
- # Authorization is required to access the resource specified.
42
- class Unauthorized < ExceptionWithResponse
43
- ErrorMessage = 'Unauthorized'
44
- end
38
+ class NotModified < ExceptionWithResponse
39
+ ErrorMessage = 'NotModified'
40
+ end
45
41
 
46
- # No resource was found at the given URL.
47
- class ResourceNotFound < ExceptionWithResponse
48
- ErrorMessage = 'Resource not found'
49
- end
42
+ # Authorization is required to access the resource specified.
43
+ class Unauthorized < ExceptionWithResponse
44
+ ErrorMessage = 'Unauthorized'
45
+ end
50
46
 
51
- # The server broke the connection prior to the request completing. Usually
52
- # this means it crashed, or sometimes that your network connection was
53
- # severed before it could complete.
54
- class ServerBrokeConnection < Exception
55
- ErrorMessage = 'Server broke connection'
56
- end
47
+ # No resource was found at the given URL.
48
+ class ResourceNotFound < ExceptionWithResponse
49
+ ErrorMessage = 'Resource not found'
50
+ end
57
51
 
58
- # The server took too long to respond.
59
- class RequestTimeout < Exception
60
- ErrorMessage = 'Request timed out'
61
- end
52
+ # The server broke the connection prior to the request completing. Usually
53
+ # this means it crashed, or sometimes that your network connection was
54
+ # severed before it could complete.
55
+ class ServerBrokeConnection < Exception
56
+ ErrorMessage = 'Server broke connection'
57
+ end
62
58
 
63
- # The request failed, meaning the remote HTTP server returned a code other
64
- # than success, unauthorized, or redirect.
65
- #
66
- # The exception message attempts to extract the error from the XML, using
67
- # format returned by Rails: <errors><error>some message</error></errors>
68
- #
69
- # You can get the status code by e.http_code, or see anything about the
70
- # response via e.response. For example, the entire result body (which is
71
- # probably an HTML error page) is e.response.body.
72
- class RequestFailed < ExceptionWithResponse
73
- def message
74
- "HTTP status code #{http_code}"
75
- end
59
+ # The server took too long to respond.
60
+ class RequestTimeout < Exception
61
+ ErrorMessage = 'Request timed out'
62
+ end
76
63
 
77
- def to_s
78
- message
79
- end
80
- end
64
+ # The request failed, meaning the remote HTTP server returned a code other
65
+ # than success, unauthorized, or redirect.
66
+ #
67
+ # The exception message attempts to extract the error from the XML, using
68
+ # format returned by Rails: <errors><error>some message</error></errors>
69
+ #
70
+ # You can get the status code by e.http_code, or see anything about the
71
+ # response via e.response. For example, the entire result body (which is
72
+ # probably an HTML error page) is e.response.body.
73
+ class RequestFailed < ExceptionWithResponse
74
+ def message
75
+ "HTTP status code #{http_code}"
76
+ end
77
+
78
+ def to_s
79
+ message
80
+ end
81
+ end
81
82
  end
82
83
 
83
84
  # backwards compatibility
84
85
  class RestClient::Request
85
- Redirect = RestClient::Redirect
86
- Unauthorized = RestClient::Unauthorized
87
- RequestFailed = RestClient::RequestFailed
86
+ Redirect = RestClient::Redirect
87
+ Unauthorized = RestClient::Unauthorized
88
+ RequestFailed = RestClient::RequestFailed
88
89
  end
@@ -1,48 +1,48 @@
1
1
  module RestClient
2
- module Mixin
3
- module Response
4
- attr_reader :net_http_res
2
+ module Mixin
3
+ module Response
4
+ attr_reader :net_http_res
5
5
 
6
- # HTTP status code, always 200 since RestClient throws exceptions for
7
- # other codes.
8
- def code
9
- @code ||= @net_http_res.code.to_i
10
- end
6
+ # HTTP status code, always 200 since RestClient throws exceptions for
7
+ # other codes.
8
+ def code
9
+ @code ||= @net_http_res.code.to_i
10
+ end
11
11
 
12
- # A hash of the headers, beautified with symbols and underscores.
13
- # e.g. "Content-type" will become :content_type.
14
- def headers
15
- @headers ||= self.class.beautify_headers(@net_http_res.to_hash)
16
- end
12
+ # A hash of the headers, beautified with symbols and underscores.
13
+ # e.g. "Content-type" will become :content_type.
14
+ def headers
15
+ @headers ||= self.class.beautify_headers(@net_http_res.to_hash)
16
+ end
17
17
 
18
- # The raw headers.
19
- def raw_headers
20
- @raw_headers ||= @net_http_res.to_hash
21
- end
18
+ # The raw headers.
19
+ def raw_headers
20
+ @raw_headers ||= @net_http_res.to_hash
21
+ end
22
22
 
23
- # Hash of cookies extracted from response headers
24
- def cookies
25
- @cookies ||= (self.headers[:set_cookie] || "").split('; ').inject({}) do |out, raw_c|
26
- key, val = raw_c.split('=')
27
- unless %w(expires domain path secure).member?(key)
28
- out[key] = val
29
- end
30
- out
31
- end
32
- end
23
+ # Hash of cookies extracted from response headers
24
+ def cookies
25
+ @cookies ||= (self.headers[:set_cookie] || "").split('; ').inject({}) do |out, raw_c|
26
+ key, val = raw_c.split('=')
27
+ unless %w(expires domain path secure).member?(key)
28
+ out[key] = val
29
+ end
30
+ out
31
+ end
32
+ end
33
33
 
34
- def self.included(receiver)
35
- receiver.extend(RestClient::Mixin::Response::ClassMethods)
36
- end
34
+ def self.included(receiver)
35
+ receiver.extend(RestClient::Mixin::Response::ClassMethods)
36
+ end
37
37
 
38
- module ClassMethods
39
- def beautify_headers(headers)
40
- headers.inject({}) do |out, (key, value)|
41
- out[key.gsub(/-/, '_').downcase.to_sym] = value.first
42
- out
43
- end
44
- end
45
- end
46
- end
47
- end
38
+ module ClassMethods
39
+ def beautify_headers(headers)
40
+ headers.inject({}) do |out, (key, value)|
41
+ out[key.gsub(/-/, '_').downcase.to_sym] = value.first
42
+ out
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
48
  end
@@ -6,16 +6,16 @@
6
6
  # http://www.missiondata.com/blog/ruby/29/streaming-data-to-s3-with-ruby/
7
7
 
8
8
  module Net
9
- class HTTP
10
- alias __request__ request
9
+ class HTTP
10
+ alias __request__ request
11
11
 
12
- def request(req, body=nil, &block)
13
- if body != nil && body.respond_to?(:read)
14
- req.body_stream = body
15
- return __request__(req, nil, &block)
16
- else
17
- return __request__(req, body, &block)
18
- end
19
- end
20
- end
12
+ def request(req, body=nil, &block)
13
+ if body != nil && body.respond_to?(:read)
14
+ req.body_stream = body
15
+ return __request__(req, nil, &block)
16
+ else
17
+ return __request__(req, body, &block)
18
+ end
19
+ end
20
+ end
21
21
  end
@@ -1,171 +1,178 @@
1
- require "tempfile"
2
- require "stringio"
3
- require "mime/types"
1
+ require 'tempfile'
2
+ require 'stringio'
3
+ require 'mime/types'
4
4
 
5
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
13
- if params.delete(:multipart) == true || has_file?(params)
14
- Multipart.new(params)
15
- else
16
- UrlEncoded.new(params)
17
- end
18
- else
19
- nil
20
- end
21
- end
22
-
23
- def has_file?(params)
24
- params.any? do |_, v|
25
- case v
26
- when Hash
27
- has_file?(v)
28
- else
29
- v.respond_to?(:path) && v.respond_to?(:read)
30
- end
31
- end
32
- end
33
-
34
- class Base
35
- def initialize(params)
36
- build_stream(params)
37
- end
38
-
39
- def build_stream(params)
40
- @stream = StringIO.new(params)
41
- @stream.seek(0)
42
- end
43
-
44
- def read(bytes=nil)
45
- @stream.read(bytes)
46
- end
47
- alias :to_s :read
48
-
49
- def escape(v)
50
- URI.escape(v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
51
- end
52
-
53
- # Flatten parameters by converting hashes of hashes to flat hashes
54
- # {keys1 => {keys2 => value}} will be transformed into {keys1[key2] => value}
55
- def flatten_params(params, parent_key = nil)
56
- result = {}
57
- params.keys.map do |key|
58
- calculated_key = parent_key ? "#{parent_key}[#{escape key}]" : escape(key)
59
- value = params[key]
60
- if value.is_a? Hash
61
- result.merge!(flatten_params(value, calculated_key))
62
- else
63
- result[calculated_key] = value
64
- end
65
- end
66
- result
67
- end
68
-
69
- def headers
70
- { 'Content-Length' => size.to_s }
71
- end
72
-
73
- def size
74
- @stream.size
75
- end
76
- alias :length :size
77
-
78
- def close
79
- @stream.close
80
- end
81
-
82
- def inspect
83
- result = to_s.inspect
84
- @stream.seek(0)
85
- result
86
- end
87
- end
88
-
89
- class UrlEncoded < Base
90
- def build_stream(params = nil)
91
- @stream = StringIO.new(flatten_params(params).map do |k,v|
92
- "#{k}=#{escape(v)}"
93
- end.join("&"))
94
- @stream.seek(0)
95
- end
96
-
97
- def headers
98
- super.merge({ 'Content-Type' => 'application/x-www-form-urlencoded' })
99
- end
100
- end
101
-
102
- class Multipart < Base
103
- EOL = "\r\n"
104
-
105
- def build_stream(params)
106
- b = "--#{boundary}"
107
-
108
- @stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
109
- @stream.write(b + EOL)
110
-
111
- if params.is_a? Hash
112
- x = flatten_params(params).to_a
113
- else
114
- x = params.to_a
115
- end
116
-
117
- last_index = x.length - 1
118
- x.each_with_index do |a, index|
119
- k, v = *a
120
- if v.respond_to?(:read) && v.respond_to?(:path)
121
- create_file_field(@stream, k,v)
122
- else
123
- create_regular_field(@stream, k,v)
124
- end
125
- @stream.write(EOL + b)
126
- @stream.write(EOL) unless last_index == index
127
- end
128
- @stream.write('--')
129
- @stream.write(EOL)
130
- @stream.seek(0)
131
- end
132
-
133
- def create_regular_field(s, k, v)
134
- s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"")
135
- s.write(EOL)
136
- s.write(EOL)
137
- s.write(v)
138
- end
139
-
140
- def create_file_field(s, k, v)
141
- begin
142
- s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"; filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
143
- s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
144
- s.write(EOL)
145
- while data = v.read(8124)
146
- s.write(data)
147
- end
148
- ensure
149
- v.close
150
- end
151
- end
152
-
153
- def mime_for(path)
154
- mime = MIME::Types.type_for path
155
- mime.empty? ? 'text/plain' : mime[0].content_type
156
- end
157
-
158
- def boundary
159
- @boundary ||= rand(1_000_000).to_s
160
- end
161
-
162
- def headers
163
- super.merge({'Content-Type' => %Q{multipart/form-data; boundary="#{boundary}"}})
164
- end
165
-
166
- def close
167
- @stream.close
168
- end
169
- end
170
- end
6
+ module Payload
7
+ extend self
8
+
9
+ def generate(params)
10
+ if params.is_a?(String)
11
+ Base.new(params)
12
+ elsif params
13
+ if params.delete(:multipart) == true || has_file?(params)
14
+ Multipart.new(params)
15
+ else
16
+ UrlEncoded.new(params)
17
+ end
18
+ else
19
+ nil
20
+ end
21
+ end
22
+
23
+ def has_file?(params)
24
+ params.any? do |_, v|
25
+ case v
26
+ when Hash
27
+ has_file?(v)
28
+ else
29
+ v.respond_to?(:path) && v.respond_to?(:read)
30
+ end
31
+ end
32
+ end
33
+
34
+ class Base
35
+ def initialize(params)
36
+ build_stream(params)
37
+ end
38
+
39
+ def build_stream(params)
40
+ @stream = StringIO.new(params)
41
+ @stream.seek(0)
42
+ end
43
+
44
+ def read(bytes=nil)
45
+ @stream.read(bytes)
46
+ end
47
+
48
+ alias :to_s :read
49
+
50
+ def escape(v)
51
+ URI.escape(v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
52
+ end
53
+
54
+ # Flatten parameters by converting hashes of hashes to flat hashes
55
+ # {keys1 => {keys2 => value}} will be transformed into {keys1[key2] => value}
56
+ def flatten_params(params, parent_key = nil)
57
+ result = {}
58
+ params.keys.map do |key|
59
+ calculated_key = parent_key ? "#{parent_key}[#{escape key}]" : escape(key)
60
+ value = params[key]
61
+ if value.is_a? Hash
62
+ result.merge!(flatten_params(value, calculated_key))
63
+ else
64
+ result[calculated_key] = value
65
+ end
66
+ end
67
+ result
68
+ end
69
+
70
+ def headers
71
+ { 'Content-Length' => size.to_s }
72
+ end
73
+
74
+ def size
75
+ @stream.size
76
+ end
77
+
78
+ alias :length :size
79
+
80
+ def close
81
+ @stream.close
82
+ end
83
+
84
+ def inspect
85
+ result = to_s.inspect
86
+ @stream.seek(0)
87
+ result
88
+ end
89
+
90
+ def short_inspect
91
+ (size > 100 ? "#{size} byte length" : inspect)
92
+ end
93
+
94
+ end
95
+
96
+ class UrlEncoded < Base
97
+ def build_stream(params = nil)
98
+ @stream = StringIO.new(flatten_params(params).map do |k, v|
99
+ "#{k}=#{escape(v)}"
100
+ end.join("&"))
101
+ @stream.seek(0)
102
+ end
103
+
104
+ def headers
105
+ super.merge({ 'Content-Type' => 'application/x-www-form-urlencoded' })
106
+ end
107
+ end
108
+
109
+ class Multipart < Base
110
+ EOL = "\r\n"
111
+
112
+ def build_stream(params)
113
+ b = "--#{boundary}"
114
+
115
+ @stream = Tempfile.new("RESTClient.Stream.#{rand(1000)}")
116
+ @stream.write(b + EOL)
117
+
118
+ if params.is_a? Hash
119
+ x = flatten_params(params).to_a
120
+ else
121
+ x = params.to_a
122
+ end
123
+
124
+ last_index = x.length - 1
125
+ x.each_with_index do |a, index|
126
+ k, v = *a
127
+ if v.respond_to?(:read) && v.respond_to?(:path)
128
+ create_file_field(@stream, k, v)
129
+ else
130
+ create_regular_field(@stream, k, v)
131
+ end
132
+ @stream.write(EOL + b)
133
+ @stream.write(EOL) unless last_index == index
134
+ end
135
+ @stream.write('--')
136
+ @stream.write(EOL)
137
+ @stream.seek(0)
138
+ end
139
+
140
+ def create_regular_field(s, k, v)
141
+ s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"")
142
+ s.write(EOL)
143
+ s.write(EOL)
144
+ s.write(v)
145
+ end
146
+
147
+ def create_file_field(s, k, v)
148
+ begin
149
+ s.write("Content-Disposition: multipart/form-data; name=\"#{k}\"; filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
150
+ s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
151
+ s.write(EOL)
152
+ while data = v.read(8124)
153
+ s.write(data)
154
+ end
155
+ ensure
156
+ v.close
157
+ end
158
+ end
159
+
160
+ def mime_for(path)
161
+ mime = MIME::Types.type_for path
162
+ mime.empty? ? 'text/plain' : mime[0].content_type
163
+ end
164
+
165
+ def boundary
166
+ @boundary ||= rand(1_000_000).to_s
167
+ end
168
+
169
+ def headers
170
+ super.merge({'Content-Type' => %Q{multipart/form-data; boundary="#{boundary}"}})
171
+ end
172
+
173
+ def close
174
+ @stream.close
175
+ end
176
+ end
177
+ end
171
178
  end