rest-client 1.8.0-x64-mingw32 → 2.0.0.rc1-x64-mingw32

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.
@@ -14,7 +14,7 @@ module RestClient
14
14
  #
15
15
  # With a timeout (seconds):
16
16
  #
17
- # RestClient::Resource.new('http://slow', :timeout => 10)
17
+ # RestClient::Resource.new('http://slow', :read_timeout => 10)
18
18
  #
19
19
  # With an open timeout (seconds):
20
20
  #
@@ -113,8 +113,8 @@ module RestClient
113
113
  options[:headers] || {}
114
114
  end
115
115
 
116
- def timeout
117
- options[:timeout]
116
+ def read_timeout
117
+ options[:read_timeout]
118
118
  end
119
119
 
120
120
  def open_timeout
@@ -2,20 +2,76 @@ module RestClient
2
2
 
3
3
  # A Response from RestClient, you can access the response body, the code or the headers.
4
4
  #
5
- module Response
5
+ class Response < String
6
6
 
7
7
  include AbstractResponse
8
8
 
9
+ # Return the HTTP response body.
10
+ #
11
+ # Future versions of RestClient will deprecate treating response objects
12
+ # directly as strings, so it will be necessary to call `.body`.
13
+ #
14
+ # @return [String]
15
+ #
9
16
  def body
10
- self
17
+ # Benchmarking suggests that "#{self}" is fastest, and that caching the
18
+ # body string in an instance variable doesn't make it enough faster to be
19
+ # worth the extra memory storage.
20
+ String.new(self)
11
21
  end
12
22
 
13
- def self.create body, net_http_res, args, request
14
- result = body || ''
15
- result.extend Response
23
+ # Convert the HTTP response body to a pure String object.
24
+ #
25
+ # @return [String]
26
+ def to_s
27
+ body
28
+ end
29
+
30
+ # Convert the HTTP response body to a pure String object.
31
+ #
32
+ # @return [String]
33
+ def to_str
34
+ body
35
+ end
36
+
37
+ def inspect
38
+ "<RestClient::Response #{code.inspect} #{body_truncated(10).inspect}>"
39
+ end
40
+
41
+ def self.create(body, net_http_res, args, request)
42
+ result = self.new(body || '')
43
+
16
44
  result.response_set_vars(net_http_res, args, request)
45
+ fix_encoding(result)
46
+
17
47
  result
18
48
  end
19
49
 
50
+ private
51
+
52
+ def self.fix_encoding(response)
53
+ charset = RestClient::Utils.get_encoding_from_headers(response.headers)
54
+ encoding = nil
55
+
56
+ begin
57
+ encoding = Encoding.find(charset) if charset
58
+ rescue ArgumentError
59
+ RestClient.log << "No such encoding: #{charset.inspect}"
60
+ end
61
+
62
+ return unless encoding
63
+
64
+ response.force_encoding(encoding)
65
+
66
+ response
67
+ end
68
+
69
+ def body_truncated(length)
70
+ if body.length > length
71
+ body[0..length] + '...'
72
+ else
73
+ body
74
+ end
75
+ end
20
76
  end
21
77
  end
@@ -0,0 +1,93 @@
1
+ module RestClient
2
+ # Various utility methods
3
+ module Utils
4
+
5
+ # Return encoding from an HTTP header hash.
6
+ #
7
+ # We use the RFC 7231 specification and do not impose a default encoding on
8
+ # text. This differs from the older RFC 2616 behavior, which specifies
9
+ # using ISO-8859-1 for text/* content types without a charset.
10
+ #
11
+ # Strings will effectively end up using `Encoding.default_external` when
12
+ # this method returns nil.
13
+ #
14
+ # @param headers [Hash]
15
+ #
16
+ # @return [String, nil] encoding Return the string encoding or nil if no
17
+ # header is found.
18
+ #
19
+ def self.get_encoding_from_headers(headers)
20
+ type_header = headers[:content_type]
21
+ return nil unless type_header
22
+
23
+ _content_type, params = cgi_parse_header(type_header)
24
+
25
+ if params.include?('charset')
26
+ return params.fetch('charset').gsub(/(\A["']*)|(["']*\z)/, '')
27
+ end
28
+
29
+ nil
30
+ end
31
+
32
+ # Parse semi-colon separated, potentially quoted header string iteratively.
33
+ #
34
+ # @private
35
+ #
36
+ def self._cgi_parseparam(s)
37
+ return enum_for(__method__, s) unless block_given?
38
+
39
+ while s[0] == ';'
40
+ s = s[1..-1]
41
+ ends = s.index(';')
42
+ while ends && ends > 0 \
43
+ && (s[0...ends].count('"') -
44
+ s[0...ends].scan('\"').count) % 2 != 0
45
+ ends = s.index(';', ends + 1)
46
+ end
47
+ if ends.nil?
48
+ ends = s.length
49
+ end
50
+ f = s[0...ends]
51
+ yield f.strip
52
+ s = s[ends..-1]
53
+ end
54
+ nil
55
+ end
56
+
57
+ # Parse a Content-type like header.
58
+ #
59
+ # Return the main content-type and a hash of options.
60
+ #
61
+ # This method was ported directly from Python's cgi.parse_header(). It
62
+ # probably doesn't read or perform particularly well in ruby.
63
+ # https://github.com/python/cpython/blob/3.4/Lib/cgi.py#L301-L331
64
+ #
65
+ #
66
+ # @param [String] line
67
+ # @return [Array(String, Hash)]
68
+ #
69
+ def self.cgi_parse_header(line)
70
+ parts = _cgi_parseparam(';' + line)
71
+ key = parts.next
72
+ pdict = {}
73
+
74
+ begin
75
+ while (p = parts.next)
76
+ i = p.index('=')
77
+ if i
78
+ name = p[0...i].strip.downcase
79
+ value = p[i+1..-1].strip
80
+ if value.length >= 2 && value[0] == '"' && value[-1] == '"'
81
+ value = value[1...-1]
82
+ value = value.gsub('\\\\', '\\').gsub('\\"', '"')
83
+ end
84
+ pdict[name] = value
85
+ end
86
+ end
87
+ rescue StopIteration
88
+ end
89
+
90
+ [key, pdict]
91
+ end
92
+ end
93
+ end
@@ -1,5 +1,6 @@
1
1
  module RestClient
2
- VERSION = '1.8.0' unless defined?(self::VERSION)
2
+ VERSION_INFO = [2, 0, 0, 'rc1'] unless defined?(self::VERSION_INFO)
3
+ VERSION = VERSION_INFO.map(&:to_s).join('.') unless defined?(self::VERSION)
3
4
 
4
5
  def self.version
5
6
  VERSION
data/rest-client.gemspec CHANGED
@@ -17,14 +17,15 @@ Gem::Specification.new do |s|
17
17
  s.summary = 'Simple HTTP and REST client for Ruby, inspired by microframework syntax for specifying actions.'
18
18
 
19
19
  s.add_development_dependency('webmock', '~> 1.4')
20
- s.add_development_dependency('rspec', '~> 2.4')
21
- s.add_development_dependency('pry')
22
- s.add_development_dependency('pry-doc')
20
+ s.add_development_dependency('rspec', '~> 2.99')
21
+ s.add_development_dependency('pry', '~> 0')
22
+ s.add_development_dependency('pry-doc', '~> 0')
23
23
  s.add_development_dependency('rdoc', '>= 2.4.2', '< 5.0')
24
+ s.add_development_dependency('rubocop', '~> 0')
24
25
 
25
26
  s.add_dependency('http-cookie', '>= 1.0.2', '< 2.0')
26
27
  s.add_dependency('mime-types', '>= 1.16', '< 3.0')
27
- s.add_dependency('netrc', '~> 0.7')
28
+ s.add_dependency('netrc', '~> 0.8')
28
29
 
29
- s.required_ruby_version = '>= 1.9.2'
30
+ s.required_ruby_version = '>= 1.9.3'
30
31
  end
data/spec/helpers.rb ADDED
@@ -0,0 +1,14 @@
1
+ module Helpers
2
+ def response_double(opts={})
3
+ double('response', {:to_hash => {}}.merge(opts))
4
+ end
5
+
6
+ def fake_stderr
7
+ original_stderr = $stderr
8
+ $stderr = StringIO.new
9
+ yield
10
+ $stderr.string
11
+ ensure
12
+ $stderr = original_stderr
13
+ end
14
+ end
@@ -0,0 +1 @@
1
+ require_relative '../spec_helper'
@@ -0,0 +1,72 @@
1
+ require_relative '_lib'
2
+ require 'json'
3
+
4
+ describe RestClient::Request do
5
+ before(:all) do
6
+ WebMock.disable!
7
+ end
8
+
9
+ after(:all) do
10
+ WebMock.enable!
11
+ end
12
+
13
+ def default_httpbin_url
14
+ # add a hack to work around java/jruby bug
15
+ # java.lang.RuntimeException: Could not generate DH keypair with backtrace
16
+ if ENV['TRAVIS_RUBY_VERSION'] == 'jruby-19mode'
17
+ 'http://httpbin.org/'
18
+ else
19
+ 'https://httpbin.org/'
20
+ end
21
+ end
22
+
23
+ def httpbin(suffix='')
24
+ url = ENV.fetch('HTTPBIN_URL', default_httpbin_url)
25
+ unless url.end_with?('/')
26
+ url += '/'
27
+ end
28
+
29
+ url + suffix
30
+ end
31
+
32
+ def execute_httpbin(suffix, opts={})
33
+ opts = {url: httpbin(suffix)}.merge(opts)
34
+ RestClient::Request.execute(opts)
35
+ end
36
+
37
+ def execute_httpbin_json(suffix, opts={})
38
+ JSON.parse(execute_httpbin(suffix, opts))
39
+ end
40
+
41
+ describe '.execute' do
42
+ it 'sends a user agent' do
43
+ data = execute_httpbin_json('user-agent', method: :get)
44
+ data['user-agent'].should match(/rest-client/)
45
+ end
46
+
47
+ it 'receives cookies on 302' do
48
+ expect {
49
+ execute_httpbin('cookies/set?foo=bar', method: :get, max_redirects: 0)
50
+ }.to raise_error(RestClient::Found) { |ex|
51
+ ex.http_code.should eq 302
52
+ ex.response.cookies['foo'].should eq 'bar'
53
+ }
54
+ end
55
+
56
+ it 'passes along cookies through 302' do
57
+ data = execute_httpbin_json('cookies/set?foo=bar', method: :get)
58
+ data.should have_key('cookies')
59
+ data['cookies']['foo'].should eq 'bar'
60
+ end
61
+
62
+ it 'handles quote wrapped cookies' do
63
+ expect {
64
+ execute_httpbin('cookies/set?foo=' + CGI.escape('"bar:baz"'),
65
+ method: :get, max_redirects: 0)
66
+ }.to raise_error(RestClient::Found) { |ex|
67
+ ex.http_code.should eq 302
68
+ ex.response.cookies['foo'].should eq '"bar:baz"'
69
+ }
70
+ end
71
+ end
72
+ end
@@ -1,4 +1,6 @@
1
- require 'spec_helper'
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '_lib'
3
+ require 'base64'
2
4
 
3
5
  describe RestClient do
4
6
 
@@ -31,5 +33,81 @@ describe RestClient do
31
33
  end
32
34
  end
33
35
 
36
+ describe 'charset parsing' do
37
+ it 'handles utf-8' do
38
+ body = "λ".force_encoding('ASCII-8BIT')
39
+ stub_request(:get, "www.example.com").to_return(
40
+ :body => body, :status => 200, :headers => {
41
+ 'Content-Type' => 'text/plain; charset=UTF-8'
42
+ })
43
+ response = RestClient.get "www.example.com"
44
+ response.encoding.should eq Encoding::UTF_8
45
+ response.valid_encoding?.should eq true
46
+ end
47
+
48
+ it 'handles windows-1252' do
49
+ body = "\xff".force_encoding('ASCII-8BIT')
50
+ stub_request(:get, "www.example.com").to_return(
51
+ :body => body, :status => 200, :headers => {
52
+ 'Content-Type' => 'text/plain; charset=windows-1252'
53
+ })
54
+ response = RestClient.get "www.example.com"
55
+ response.encoding.should eq Encoding::WINDOWS_1252
56
+ response.encode('utf-8').should eq "ÿ"
57
+ response.valid_encoding?.should eq true
58
+ end
59
+
60
+ it 'handles binary' do
61
+ body = "\xfe".force_encoding('ASCII-8BIT')
62
+ stub_request(:get, "www.example.com").to_return(
63
+ :body => body, :status => 200, :headers => {
64
+ 'Content-Type' => 'application/octet-stream; charset=binary'
65
+ })
66
+ response = RestClient.get "www.example.com"
67
+ response.encoding.should eq Encoding::BINARY
68
+ lambda {
69
+ response.encode('utf-8')
70
+ }.should raise_error(Encoding::UndefinedConversionError)
71
+ response.valid_encoding?.should eq true
72
+ end
73
+
74
+ it 'handles euc-jp' do
75
+ body = "\xA4\xA2\xA4\xA4\xA4\xA6\xA4\xA8\xA4\xAA".
76
+ force_encoding(Encoding::BINARY)
77
+ body_utf8 = 'あいうえお'
78
+ body_utf8.encoding.should eq Encoding::UTF_8
79
+
80
+ stub_request(:get, 'www.example.com').to_return(
81
+ :body => body, :status => 200, :headers => {
82
+ 'Content-Type' => 'text/plain; charset=EUC-JP'
83
+ })
84
+ response = RestClient.get 'www.example.com'
85
+ response.encoding.should eq Encoding::EUC_JP
86
+ response.valid_encoding?.should eq true
87
+ response.length.should eq 5
88
+ response.encode('utf-8').should eq body_utf8
89
+ end
34
90
 
91
+ it 'defaults to Encoding.default_external' do
92
+ stub_request(:get, 'www.example.com').to_return(
93
+ body: 'abc', status: 200, headers: {
94
+ 'Content-Type' => 'text/plain'
95
+ })
96
+
97
+ response = RestClient.get 'www.example.com'
98
+ response.encoding.should eq Encoding.default_external
99
+ end
100
+
101
+ it 'leaves images as binary' do
102
+ gif = Base64.strict_decode64('R0lGODlhAQABAAAAADs=')
103
+
104
+ stub_request(:get, 'www.example.com').to_return(
105
+ body: gif, status: 200, headers: {
106
+ 'Content-Type' => 'image/gif'
107
+ })
108
+
109
+ response = RestClient.get 'www.example.com'
110
+ response.encoding.should eq Encoding::BINARY
111
+ end
112
+ end
35
113
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require_relative '_lib'
2
2
 
3
3
  describe RestClient::Request do
4
4
  before(:all) do
@@ -101,4 +101,27 @@ describe RestClient::Request do
101
101
  expect { request.execute }.to_not raise_error
102
102
  end
103
103
  end
104
+
105
+ describe "timeouts" do
106
+ it "raises OpenTimeout when it hits an open timeout" do
107
+ request = RestClient::Request.new(
108
+ :method => :get,
109
+ :url => 'http://www.mozilla.org',
110
+ :open_timeout => 1e-10,
111
+ )
112
+ expect { request.execute }.to(
113
+ raise_error(RestClient::Exceptions::OpenTimeout))
114
+ end
115
+
116
+ it "raises ReadTimeout when it hits a read timeout via :read_timeout" do
117
+ request = RestClient::Request.new(
118
+ :method => :get,
119
+ :url => 'https://www.mozilla.org',
120
+ :read_timeout => 1e-10,
121
+ )
122
+ expect { request.execute }.to(
123
+ raise_error(RestClient::Exceptions::ReadTimeout))
124
+ end
125
+ end
126
+
104
127
  end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,22 @@
1
1
  require 'webmock/rspec'
2
- require 'restclient'
2
+ require 'rest-client'
3
+
4
+ require_relative './helpers'
5
+
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+
10
+ # Run specs in random order to surface order dependencies. If you find an
11
+ # order dependency and want to debug it, you can fix the order by providing
12
+ # the seed, which is printed after each run.
13
+ # --seed 1234
14
+ config.order = 'random'
15
+
16
+ # add helpers
17
+ config.include Helpers, :include_helpers
18
+
19
+ config.mock_with :rspec do |mocks|
20
+ mocks.yield_receiver_to_any_instance_implementation_blocks = true
21
+ end
22
+ end