rest-client 1.6.14 → 1.7.0.rc1
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.
- checksums.yaml +5 -5
- data/.gitignore +4 -3
- data/.travis.yml +12 -1
- data/AUTHORS +10 -4
- data/Gemfile +5 -1
- data/LICENSE +21 -0
- data/README.rdoc +27 -2
- data/Rakefile +78 -10
- data/bin/restclient +1 -1
- data/history.md +22 -16
- data/lib/restclient.rb +2 -8
- data/lib/restclient/exceptions.rb +7 -2
- data/lib/restclient/platform.rb +2 -1
- data/lib/restclient/request.rb +270 -48
- data/lib/restclient/response.rb +0 -2
- data/lib/restclient/version.rb +1 -1
- data/lib/restclient/windows.rb +8 -0
- data/lib/restclient/windows/root_certs.rb +105 -0
- data/rest-client.gemspec +14 -10
- data/rest-client.windows.gemspec +19 -0
- data/spec/integration/capath_verisign/415660c1.0 +14 -0
- data/spec/integration/capath_verisign/7651b327.0 +14 -0
- data/spec/integration/capath_verisign/README +8 -0
- data/spec/integration/capath_verisign/verisign.crt +14 -0
- data/spec/{integration_spec.rb → integration/integration_spec.rb} +2 -5
- data/spec/integration/request_spec.rb +46 -17
- data/spec/{base.rb → spec_helper.rb} +2 -3
- data/spec/{abstract_response_spec.rb → unit/abstract_response_spec.rb} +1 -1
- data/spec/{exceptions_spec.rb → unit/exceptions_spec.rb} +1 -4
- data/spec/{master_shake.jpg → unit/master_shake.jpg} +0 -0
- data/spec/{payload_spec.rb → unit/payload_spec.rb} +2 -1
- data/spec/{raw_response_spec.rb → unit/raw_response_spec.rb} +1 -1
- data/spec/{request2_spec.rb → unit/request2_spec.rb} +1 -4
- data/spec/{request_spec.rb → unit/request_spec.rb} +386 -9
- data/spec/{resource_spec.rb → unit/resource_spec.rb} +1 -4
- data/spec/{response_spec.rb → unit/response_spec.rb} +1 -4
- data/spec/{restclient_spec.rb → unit/restclient_spec.rb} +7 -1
- data/spec/unit/windows/root_certs_spec.rb +22 -0
- metadata +93 -58
- data/lib/restclient/net_http_ext.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bf3dedbad07361a8b9256ccfe76b123db48c00b3
|
4
|
+
data.tar.gz: 74cc0618213110d4aababe7911c367e84e2f813b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1230bc04a0a791ff416e9592dbd9ecf96bc315f39ddcebdd41266f4e84dbfd5ded56a79796a13e4b4598f2a0443c09dc8e925a73351e4d77bebd62d43338dc3f
|
7
|
+
data.tar.gz: 3a90bb3dc2297d6a6206780c4b088633ec098c535195592b86e481902cd794974ce0552b668ab75f1992eda0615c6ffc1282e76f835de72fdd5d8836ff89b491
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,3 +1,14 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- "1.
|
3
|
+
- "1.9.2"
|
4
|
+
- "1.9.3"
|
5
|
+
- "2.0.0"
|
6
|
+
# Forgo 2.1.0 until Travis has a satisfactory fix for
|
7
|
+
# https://github.com/travis-ci/travis-ci/issues/2220
|
8
|
+
- "2.1" # always the latest 2.1.x
|
9
|
+
- "jruby-19mode"
|
10
|
+
script:
|
11
|
+
bundle exec rake test
|
12
|
+
branches:
|
13
|
+
except:
|
14
|
+
- "readme-edits"
|
data/AUTHORS
CHANGED
@@ -11,12 +11,14 @@ Braintree
|
|
11
11
|
Brian Donovan
|
12
12
|
Caleb Land
|
13
13
|
Chris Dinn
|
14
|
+
Chris Frohoff
|
14
15
|
Chris Green
|
15
16
|
Coda Hale
|
16
17
|
Crawford
|
17
18
|
Cyril Rohr
|
18
19
|
Dan Mayer
|
19
20
|
David Backeus
|
21
|
+
David Perkowski
|
20
22
|
Dmitri Dolguikh
|
21
23
|
Dusty Doris
|
22
24
|
Dylan Egan
|
@@ -32,16 +34,19 @@ Hiro Asari
|
|
32
34
|
Hugh McGowan
|
33
35
|
Ian Warshak
|
34
36
|
Ivan Makfinsky
|
37
|
+
JH. Chabran
|
35
38
|
James Edward Gray II
|
36
39
|
Jari Bakken
|
37
40
|
Jeff Remer
|
38
|
-
|
41
|
+
Jeffrey Hardy
|
42
|
+
Jeremy Kemper
|
39
43
|
John Barnette
|
40
44
|
Jon Rowe
|
41
45
|
Jordi Massaguer Pla
|
42
46
|
Juan Alvarez
|
43
47
|
Julien Kirch
|
44
48
|
Justin Coyne
|
49
|
+
Justin Lambert
|
45
50
|
Keith Rarick
|
46
51
|
Kenichi Kamiya
|
47
52
|
Kevin Read
|
@@ -51,7 +56,7 @@ Lars Gierth
|
|
51
56
|
Lawrence Leonard Gilbert
|
52
57
|
Lee Jarvis
|
53
58
|
Lennon Day-Reynolds
|
54
|
-
|
59
|
+
Marc-André Cournoyer
|
55
60
|
Matthew Manning
|
56
61
|
Michael Klett
|
57
62
|
Mike Fletcher
|
@@ -62,14 +67,15 @@ Oscar Del Ben
|
|
62
67
|
Pablo Astigarraga
|
63
68
|
Paul Dlug
|
64
69
|
Pedro Belo
|
65
|
-
rafael.ssouza
|
66
70
|
Philip Corliss
|
71
|
+
Pierre-Louis Gottfrois
|
72
|
+
Rafael Ssouza
|
67
73
|
Rick "technoweenie"
|
68
74
|
Robert Eanes
|
69
75
|
Rodrigo Panachi
|
70
76
|
Syl Turner
|
71
77
|
T. Watanabe
|
72
78
|
Tekin
|
73
|
-
tpresa
|
74
79
|
W. Andrew Loe III
|
75
80
|
Waynn Lue
|
81
|
+
tpresa
|
data/Gemfile
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2008-2014 Rest Client Authors
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
= REST Client -- simple DSL for accessing HTTP and REST resources
|
2
2
|
|
3
|
-
Build status: {<img src="https://travis-ci.org/rest-client/rest-client.
|
3
|
+
Build status: {<img src="https://travis-ci.org/rest-client/rest-client.png" />}[https://travis-ci.org/rest-client/rest-client]
|
4
|
+
|
4
5
|
|
5
6
|
A simple HTTP and REST client for Ruby, inspired by the Sinatra's microframework style
|
6
7
|
of specifying actions: get, put, post, delete.
|
@@ -8,6 +9,27 @@ of specifying actions: get, put, post, delete.
|
|
8
9
|
* Main page: https://github.com/rest-client/rest-client
|
9
10
|
* Mailing list: rest.client@librelist.com (send a mail to subscribe).
|
10
11
|
|
12
|
+
== Requirements
|
13
|
+
|
14
|
+
MRI Ruby 1.9.2 and newer are supported. Alternative interpreters compatible with
|
15
|
+
1.9.1+ should work as well.
|
16
|
+
|
17
|
+
Ruby 1.8.7 is no longer supported. That's because the Ruby 1.8.7 interpreter
|
18
|
+
itself no longer has official support, _not_ _even_ _security_ _patches!_ If you
|
19
|
+
have been putting off upgrading your servers, now is the time.
|
20
|
+
({More info is on the Ruby developers'
|
21
|
+
blog.}[http://www.ruby-lang.org/en/news/2013/06/30/we-retire-1-8-7/])
|
22
|
+
|
23
|
+
The rest-client gem depends on these other gems for installation and usage:
|
24
|
+
|
25
|
+
* {mime-types}[http://rubygems.org/gems/mime-types]
|
26
|
+
* {netrc}[http://rubygems.org/gems/netrc]
|
27
|
+
* {rdoc}[http://rubygems.org/gems/rdoc]
|
28
|
+
|
29
|
+
If you want to hack on the code, you should also have {the Bundler
|
30
|
+
gem}[http://bundler.io/] installed so it can manage all necessary development
|
31
|
+
dependencies for you.
|
32
|
+
|
11
33
|
== Usage: Raw URL
|
12
34
|
|
13
35
|
require 'rest_client'
|
@@ -59,7 +81,7 @@ This does two things for you:
|
|
59
81
|
|
60
82
|
If you are sending params that do not contain a File object but the payload needs to be multipart then:
|
61
83
|
|
62
|
-
RestClient.post '/data', :foo => 'bar', :multipart => true
|
84
|
+
RestClient.post '/data', {:foo => 'bar', :multipart => true}
|
63
85
|
|
64
86
|
== Usage: ActiveResource-Style
|
65
87
|
|
@@ -298,3 +320,6 @@ Released under the MIT License: http://www.opensource.org/licenses/mit-license.p
|
|
298
320
|
"Master Shake" photo (http://www.flickr.com/photos/solgrundy/924205581/) by
|
299
321
|
"SolGrundy"; used under terms of the Creative Commons Attribution-ShareAlike 2.0
|
300
322
|
Generic license (http://creativecommons.org/licenses/by-sa/2.0/)
|
323
|
+
|
324
|
+
Code for reading Windows root certificate store derived from work by Puppet;
|
325
|
+
used under terms of the Apache License, Version 2.0.
|
data/Rakefile
CHANGED
@@ -1,38 +1,106 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# load `rake build/install/release tasks'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require_relative './lib/restclient/version'
|
4
|
+
|
5
|
+
namespace :ruby do
|
6
|
+
Bundler::GemHelper.install_tasks(:name => 'rest-client')
|
5
7
|
end
|
6
8
|
|
7
9
|
require "rspec/core/rake_task"
|
8
10
|
|
9
11
|
desc "Run all specs"
|
10
|
-
|
12
|
+
RSpec::Core::RakeTask.new('spec')
|
11
13
|
|
12
14
|
desc "Run unit specs"
|
13
15
|
RSpec::Core::RakeTask.new('spec:unit') do |t|
|
14
|
-
t.pattern =
|
16
|
+
t.pattern = 'spec/unit/*_spec.rb'
|
15
17
|
end
|
16
18
|
|
17
19
|
desc "Run integration specs"
|
18
20
|
RSpec::Core::RakeTask.new('spec:integration') do |t|
|
19
|
-
t.pattern =
|
21
|
+
t.pattern = 'spec/integration/*_spec.rb'
|
20
22
|
end
|
21
23
|
|
22
24
|
desc "Print specdocs"
|
23
25
|
RSpec::Core::RakeTask.new(:doc) do |t|
|
24
26
|
t.rspec_opts = ["--format", "specdoc", "--dry-run"]
|
25
|
-
t.pattern =
|
27
|
+
t.pattern = 'spec/**/*_spec.rb'
|
26
28
|
end
|
27
29
|
|
28
30
|
desc "Run all examples with RCov"
|
29
31
|
RSpec::Core::RakeTask.new('rcov') do |t|
|
30
|
-
t.pattern =
|
32
|
+
t.pattern = 'spec/*_spec.rb'
|
31
33
|
t.rcov = true
|
32
34
|
t.rcov_opts = ['--exclude', 'examples']
|
33
35
|
end
|
34
36
|
|
35
|
-
task :default
|
37
|
+
task :default do
|
38
|
+
sh 'rake -T'
|
39
|
+
end
|
40
|
+
|
41
|
+
def alias_task(alias_task, original)
|
42
|
+
desc "Alias for rake #{original}"
|
43
|
+
task alias_task, Rake.application[original].arg_names => original
|
44
|
+
end
|
45
|
+
alias_task(:test, :spec)
|
46
|
+
|
47
|
+
############################
|
48
|
+
|
49
|
+
WindowsPlatforms = %w{x86-mingw32 x64-mingw32 x86-mswin32}
|
50
|
+
|
51
|
+
namespace :all do
|
52
|
+
|
53
|
+
desc "Build rest-client #{RestClient::VERSION} for all platforms"
|
54
|
+
task :build => ['ruby:build'] + \
|
55
|
+
WindowsPlatforms.map {|p| "windows:#{p}:build"}
|
56
|
+
|
57
|
+
desc "Create tag v#{RestClient::VERSION} and for all platforms build and push " \
|
58
|
+
"rest-client #{RestClient::VERSION} to Rubygems"
|
59
|
+
task :release => ['build', 'ruby:release'] + \
|
60
|
+
WindowsPlatforms.map {|p| "windows:#{p}:push"}
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
namespace :windows do
|
65
|
+
spec_path = File.join(File.dirname(__FILE__), 'rest-client.windows.gemspec')
|
66
|
+
|
67
|
+
WindowsPlatforms.each do |platform|
|
68
|
+
namespace platform do
|
69
|
+
gem_filename = "rest-client-#{RestClient::VERSION}-#{platform}.gem"
|
70
|
+
base = File.dirname(__FILE__)
|
71
|
+
pkg_dir = File.join(base, 'pkg')
|
72
|
+
gem_file_path = File.join(pkg_dir, gem_filename)
|
73
|
+
|
74
|
+
desc "Build #{gem_filename} into the pkg directory"
|
75
|
+
task 'build' do
|
76
|
+
orig_platform = ENV['BUILD_PLATFORM']
|
77
|
+
begin
|
78
|
+
ENV['BUILD_PLATFORM'] = platform
|
79
|
+
|
80
|
+
sh("gem build -V #{spec_path}") do |ok, res|
|
81
|
+
if ok
|
82
|
+
FileUtils.mkdir_p(pkg_dir)
|
83
|
+
FileUtils.mv(File.join(base, gem_filename), pkg_dir)
|
84
|
+
Bundler.ui.confirm("rest-client #{RestClient::VERSION} " +
|
85
|
+
"built to pkg/#{gem_filename}")
|
86
|
+
else
|
87
|
+
abort "Command `gem build` failed: #{res}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
ensure
|
92
|
+
ENV['BUILD_PLATFORM'] = orig_platform
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
desc "Push #{gem_filename} to Rubygems"
|
97
|
+
task 'push' do
|
98
|
+
sh("gem push #{gem_file_path}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
36
104
|
|
37
105
|
############################
|
38
106
|
|
data/bin/restclient
CHANGED
data/history.md
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
-
# 1.
|
2
|
-
|
3
|
-
- This release
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
-
|
1
|
+
# 1.7.0
|
2
|
+
|
3
|
+
- This release drops support for Ruby 1.8.7 and breaks compatibility in a few
|
4
|
+
other relatively minor ways
|
5
|
+
- Upgrade to mime-types ~> 2.0
|
6
|
+
- Don't CGI.unescape cookie values sent to the server (issue #89)
|
7
|
+
- Add support for reading credentials from netrc
|
8
|
+
- Lots of SSL changes and enhancements: (#268)
|
9
|
+
- Enable peer verification by default (setting `VERIFY_PEER` with OpenSSL)
|
10
|
+
- By default, use the system default certificate store for SSL verification,
|
11
|
+
even on Windows (this uses a separate Windows build that pulls in ffi)
|
12
|
+
- Add support for SSL `ca_path`
|
13
|
+
- Add support for SSL `cert_store`
|
14
|
+
- Add support for SSL `verify_callback` (with some caveats for jruby, OS X, #277)
|
15
|
+
- Add support for SSL ciphers, and choose secure ones by default
|
16
|
+
- Run tests under travis
|
17
|
+
- Several other bugfixes and test improvements
|
18
|
+
- Convert Errno::ETIMEDOUT to RestClient::RequestTimeout
|
19
|
+
- Handle more HTTP response codes from recent standards
|
20
|
+
- Save raw responses to binary mode tempfile (#110)
|
21
|
+
- Disable timeouts with :timeout => nil rather than :timeout => -1
|
22
|
+
- Drop all Net::HTTP monkey patches
|
17
23
|
|
18
24
|
# 1.6.8
|
19
25
|
|
data/lib/restclient.rb
CHANGED
@@ -1,13 +1,7 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'zlib'
|
3
3
|
require 'stringio'
|
4
|
-
|
5
|
-
begin
|
6
|
-
require 'net/https'
|
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"
|
10
|
-
end
|
4
|
+
require 'net/https'
|
11
5
|
|
12
6
|
require File.dirname(__FILE__) + '/restclient/version'
|
13
7
|
require File.dirname(__FILE__) + '/restclient/platform'
|
@@ -18,7 +12,7 @@ require File.dirname(__FILE__) + '/restclient/response'
|
|
18
12
|
require File.dirname(__FILE__) + '/restclient/raw_response'
|
19
13
|
require File.dirname(__FILE__) + '/restclient/resource'
|
20
14
|
require File.dirname(__FILE__) + '/restclient/payload'
|
21
|
-
require File.dirname(__FILE__) + '/restclient/
|
15
|
+
require File.dirname(__FILE__) + '/restclient/windows'
|
22
16
|
|
23
17
|
# This module's static methods are the entry point for using the REST client.
|
24
18
|
#
|
@@ -40,13 +40,16 @@ module RestClient
|
|
40
40
|
415 => 'Unsupported Media Type',
|
41
41
|
416 => 'Requested Range Not Satisfiable',
|
42
42
|
417 => 'Expectation Failed',
|
43
|
-
418 => 'I\'m A Teapot',
|
43
|
+
418 => 'I\'m A Teapot', #RFC2324
|
44
44
|
421 => 'Too Many Connections From This IP',
|
45
45
|
422 => 'Unprocessable Entity', #WebDAV
|
46
46
|
423 => 'Locked', #WebDAV
|
47
47
|
424 => 'Failed Dependency', #WebDAV
|
48
48
|
425 => 'Unordered Collection', #WebDAV
|
49
49
|
426 => 'Upgrade Required',
|
50
|
+
428 => 'Precondition Required', #RFC6585
|
51
|
+
429 => 'Too Many Requests', #RFC6585
|
52
|
+
431 => 'Request Header Fields Too Large', #RFC6585
|
50
53
|
449 => 'Retry With', #Microsoft
|
51
54
|
450 => 'Blocked By Windows Parental Controls', #Microsoft
|
52
55
|
|
@@ -59,7 +62,9 @@ module RestClient
|
|
59
62
|
506 => 'Variant Also Negotiates',
|
60
63
|
507 => 'Insufficient Storage', #WebDAV
|
61
64
|
509 => 'Bandwidth Limit Exceeded', #Apache
|
62
|
-
510 => 'Not Extended'
|
65
|
+
510 => 'Not Extended',
|
66
|
+
511 => 'Network Authentication Required', # RFC6585
|
67
|
+
}
|
63
68
|
|
64
69
|
# Compatibility : make the Response act like a Net::HTTPResponse when needed
|
65
70
|
module ResponseForException
|
data/lib/restclient/platform.rb
CHANGED
data/lib/restclient/request.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'tempfile'
|
2
2
|
require 'mime/types'
|
3
3
|
require 'cgi'
|
4
|
+
require 'netrc'
|
5
|
+
require 'set'
|
4
6
|
|
5
7
|
module RestClient
|
6
8
|
# This class is used internally by RestClient to send the request, but you can also
|
@@ -19,22 +21,87 @@ module RestClient
|
|
19
21
|
# * :block_response call the provided block with the HTTPResponse as parameter
|
20
22
|
# * :raw_response return a low-level RawResponse instead of a Response
|
21
23
|
# * :max_redirects maximum number of redirections (default to 10)
|
22
|
-
# * :verify_ssl enable ssl verification, possible values are constants from
|
23
|
-
#
|
24
|
-
# * :
|
25
|
-
#
|
24
|
+
# * :verify_ssl enable ssl verification, possible values are constants from
|
25
|
+
# OpenSSL::SSL::VERIFY_*, defaults to OpenSSL::SSL::VERIFY_PEER
|
26
|
+
# * :timeout and :open_timeout are how long to wait for a response and to
|
27
|
+
# open a connection, in seconds. Pass nil to disable the timeout.
|
28
|
+
# * :ssl_client_cert, :ssl_client_key, :ssl_ca_file, :ssl_ca_path,
|
29
|
+
# :ssl_cert_store, :ssl_verify_callback, :ssl_verify_callback_warnings
|
30
|
+
# * :ssl_version specifies the SSL version for the underlying Net::HTTP connection
|
31
|
+
# * :ssl_ciphers sets SSL ciphers for the connection. See
|
32
|
+
# OpenSSL::SSL::SSLContext#ciphers=
|
26
33
|
class Request
|
27
34
|
|
28
35
|
attr_reader :method, :url, :headers, :cookies,
|
29
36
|
:payload, :user, :password, :timeout, :max_redirects,
|
30
|
-
:open_timeout, :raw_response, :
|
31
|
-
:
|
32
|
-
:ssl_verify_callback, :ssl_verify_callback_warnings
|
37
|
+
:open_timeout, :raw_response, :processed_headers, :args,
|
38
|
+
:ssl_opts
|
33
39
|
|
34
40
|
def self.execute(args, & block)
|
35
41
|
new(args).execute(& block)
|
36
42
|
end
|
37
43
|
|
44
|
+
# This is similar to the list now in ruby core, but adds HIGH and RC4-MD5
|
45
|
+
# for better compatibility (similar to Firefox) and moves AES-GCM cipher
|
46
|
+
# suites above DHE/ECDHE CBC suites (similar to Chromium).
|
47
|
+
# https://github.com/ruby/ruby/commit/699b209cf8cf11809620e12985ad33ae33b119ee
|
48
|
+
#
|
49
|
+
# This list will be used by default if the Ruby global OpenSSL default
|
50
|
+
# ciphers appear to be a weak list.
|
51
|
+
DefaultCiphers = %w{
|
52
|
+
!aNULL
|
53
|
+
!eNULL
|
54
|
+
!EXPORT
|
55
|
+
!SSLV2
|
56
|
+
!LOW
|
57
|
+
|
58
|
+
ECDHE-ECDSA-AES128-GCM-SHA256
|
59
|
+
ECDHE-RSA-AES128-GCM-SHA256
|
60
|
+
ECDHE-ECDSA-AES256-GCM-SHA384
|
61
|
+
ECDHE-RSA-AES256-GCM-SHA384
|
62
|
+
DHE-RSA-AES128-GCM-SHA256
|
63
|
+
DHE-DSS-AES128-GCM-SHA256
|
64
|
+
DHE-RSA-AES256-GCM-SHA384
|
65
|
+
DHE-DSS-AES256-GCM-SHA384
|
66
|
+
AES128-GCM-SHA256
|
67
|
+
AES256-GCM-SHA384
|
68
|
+
ECDHE-ECDSA-AES128-SHA256
|
69
|
+
ECDHE-RSA-AES128-SHA256
|
70
|
+
ECDHE-ECDSA-AES128-SHA
|
71
|
+
ECDHE-RSA-AES128-SHA
|
72
|
+
ECDHE-ECDSA-AES256-SHA384
|
73
|
+
ECDHE-RSA-AES256-SHA384
|
74
|
+
ECDHE-ECDSA-AES256-SHA
|
75
|
+
ECDHE-RSA-AES256-SHA
|
76
|
+
DHE-RSA-AES128-SHA256
|
77
|
+
DHE-RSA-AES256-SHA256
|
78
|
+
DHE-RSA-AES128-SHA
|
79
|
+
DHE-RSA-AES256-SHA
|
80
|
+
DHE-DSS-AES128-SHA256
|
81
|
+
DHE-DSS-AES256-SHA256
|
82
|
+
DHE-DSS-AES128-SHA
|
83
|
+
DHE-DSS-AES256-SHA
|
84
|
+
AES128-SHA256
|
85
|
+
AES256-SHA256
|
86
|
+
AES128-SHA
|
87
|
+
AES256-SHA
|
88
|
+
ECDHE-ECDSA-RC4-SHA
|
89
|
+
ECDHE-RSA-RC4-SHA
|
90
|
+
RC4-SHA
|
91
|
+
|
92
|
+
HIGH
|
93
|
+
+RC4
|
94
|
+
RC4-MD5
|
95
|
+
}.join(":")
|
96
|
+
|
97
|
+
# A set of weak default ciphers that we will override by default.
|
98
|
+
WeakDefaultCiphers = Set.new([
|
99
|
+
"ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
|
100
|
+
])
|
101
|
+
|
102
|
+
SSLOptionList = %w{client_cert client_key ca_file ca_path cert_store
|
103
|
+
version ciphers verify_callback verify_callback_warnings}
|
104
|
+
|
38
105
|
def initialize args
|
39
106
|
@method = args[:method] or raise ArgumentError, "must pass :method"
|
40
107
|
@headers = args[:headers] || {}
|
@@ -47,16 +114,57 @@ module RestClient
|
|
47
114
|
@payload = Payload.generate(args[:payload])
|
48
115
|
@user = args[:user]
|
49
116
|
@password = args[:password]
|
50
|
-
|
51
|
-
|
117
|
+
if args.include?(:timeout)
|
118
|
+
@timeout = args[:timeout]
|
119
|
+
end
|
120
|
+
if args.include?(:open_timeout)
|
121
|
+
@open_timeout = args[:open_timeout]
|
122
|
+
end
|
52
123
|
@block_response = args[:block_response]
|
53
124
|
@raw_response = args[:raw_response] || false
|
54
|
-
|
55
|
-
@
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
125
|
+
|
126
|
+
@ssl_opts = {}
|
127
|
+
|
128
|
+
if args.include?(:verify_ssl)
|
129
|
+
v_ssl = args.fetch(:verify_ssl)
|
130
|
+
if v_ssl
|
131
|
+
if v_ssl == true
|
132
|
+
# interpret :verify_ssl => true as VERIFY_PEER
|
133
|
+
@ssl_opts[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
|
134
|
+
else
|
135
|
+
# otherwise pass through any truthy values
|
136
|
+
@ssl_opts[:verify_ssl] = v_ssl
|
137
|
+
end
|
138
|
+
else
|
139
|
+
# interpret all falsy :verify_ssl values as VERIFY_NONE
|
140
|
+
@ssl_opts[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
|
141
|
+
end
|
142
|
+
else
|
143
|
+
# if :verify_ssl was not passed, default to VERIFY_PEER
|
144
|
+
@ssl_opts[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
|
145
|
+
end
|
146
|
+
|
147
|
+
SSLOptionList.each do |key|
|
148
|
+
source_key = ('ssl_' + key).to_sym
|
149
|
+
if args.has_key?(source_key)
|
150
|
+
@ssl_opts[key.to_sym] = args.fetch(source_key)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# If there's no CA file, CA path, or cert store provided, use default
|
155
|
+
if !ssl_ca_file && !ssl_ca_path && !@ssl_opts.include?(:cert_store)
|
156
|
+
@ssl_opts[:cert_store] = self.class.default_ssl_cert_store
|
157
|
+
end
|
158
|
+
|
159
|
+
unless @ssl_opts.include?(:ciphers)
|
160
|
+
# If we're on a Ruby version that has insecure default ciphers,
|
161
|
+
# override it with our default list.
|
162
|
+
if WeakDefaultCiphers.include?(
|
163
|
+
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.fetch(:ciphers))
|
164
|
+
@ssl_opts[:ciphers] = DefaultCiphers
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
60
168
|
@tf = nil # If you are a raw request, this is your tempfile
|
61
169
|
@max_redirects = args[:max_redirects] || 10
|
62
170
|
@processed_headers = make_headers headers
|
@@ -70,6 +178,16 @@ module RestClient
|
|
70
178
|
payload.close if payload
|
71
179
|
end
|
72
180
|
|
181
|
+
# SSL-related options
|
182
|
+
def verify_ssl
|
183
|
+
@ssl_opts.fetch(:verify_ssl)
|
184
|
+
end
|
185
|
+
SSLOptionList.each do |key|
|
186
|
+
define_method('ssl_' + key) do
|
187
|
+
@ssl_opts[key.to_sym]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
73
191
|
# Extract the query parameters and append them to the url
|
74
192
|
def process_url_params url, headers
|
75
193
|
url_params = {}
|
@@ -91,13 +209,46 @@ module RestClient
|
|
91
209
|
|
92
210
|
def make_headers user_headers
|
93
211
|
unless @cookies.empty?
|
94
|
-
|
212
|
+
|
213
|
+
# Validate that the cookie names and values look sane. If you really
|
214
|
+
# want to pass scary characters, just set the Cookie header directly.
|
215
|
+
# RFC6265 is actually much more restrictive than we are.
|
216
|
+
@cookies.each do |key, val|
|
217
|
+
unless valid_cookie_key?(key)
|
218
|
+
raise ArgumentError.new("Invalid cookie name: #{key.inspect}")
|
219
|
+
end
|
220
|
+
unless valid_cookie_value?(val)
|
221
|
+
raise ArgumentError.new("Invalid cookie value: #{val.inspect}")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
user_headers[:cookie] = @cookies.map { |key, val| "#{key}=#{val}" }.sort.join('; ')
|
95
226
|
end
|
96
227
|
headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
|
97
228
|
headers.merge!(@payload.headers) if @payload
|
98
229
|
headers
|
99
230
|
end
|
100
231
|
|
232
|
+
# Do some sanity checks on cookie keys.
|
233
|
+
#
|
234
|
+
# Properly it should be a valid TOKEN per RFC 2616, but lots of servers are
|
235
|
+
# more liberal.
|
236
|
+
#
|
237
|
+
# Disallow the empty string as well as keys containing control characters,
|
238
|
+
# equals sign, semicolon, comma, or space.
|
239
|
+
#
|
240
|
+
def valid_cookie_key?(string)
|
241
|
+
return false if string.empty?
|
242
|
+
|
243
|
+
! Regexp.new('[\x0-\x1f\x7f=;, ]').match(string)
|
244
|
+
end
|
245
|
+
|
246
|
+
# Validate cookie values. Rather than following RFC 6265, allow anything
|
247
|
+
# but control characters, comma, and semicolon.
|
248
|
+
def valid_cookie_value?(value)
|
249
|
+
! Regexp.new('[\x0-\x1f\x7f,;]').match(value)
|
250
|
+
end
|
251
|
+
|
101
252
|
def net_http_class
|
102
253
|
if RestClient.proxy
|
103
254
|
proxy_uri = URI.parse(RestClient.proxy)
|
@@ -111,6 +262,15 @@ module RestClient
|
|
111
262
|
Net::HTTP.const_get(method.to_s.capitalize)
|
112
263
|
end
|
113
264
|
|
265
|
+
def net_http_do_request(http, req, body=nil, &block)
|
266
|
+
if body != nil && body.respond_to?(:read)
|
267
|
+
req.body_stream = body
|
268
|
+
return http.request(req, nil, &block)
|
269
|
+
else
|
270
|
+
return http.request(req, body, &block)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
114
274
|
def parse_url(url)
|
115
275
|
url = "http://#{url}" unless url.match(/^http/)
|
116
276
|
URI.parse(url)
|
@@ -120,6 +280,9 @@ module RestClient
|
|
120
280
|
uri = parse_url(url)
|
121
281
|
@user = CGI.unescape(uri.user) if uri.user
|
122
282
|
@password = CGI.unescape(uri.password) if uri.password
|
283
|
+
if !@user && !@password
|
284
|
+
@user, @password = Netrc.read[uri.host]
|
285
|
+
end
|
123
286
|
uri
|
124
287
|
end
|
125
288
|
|
@@ -140,6 +303,30 @@ module RestClient
|
|
140
303
|
end
|
141
304
|
end
|
142
305
|
|
306
|
+
# Return a certificate store that can be used to validate certificates with
|
307
|
+
# the system certificate authorities. This will probably not do anything on
|
308
|
+
# OS X, which monkey patches OpenSSL in terrible ways to insert its own
|
309
|
+
# validation. On most *nix platforms, this will add the system certifcates
|
310
|
+
# using OpenSSL::X509::Store#set_default_paths. On Windows, this will use
|
311
|
+
# RestClient::Windows::RootCerts to look up the CAs trusted by the system.
|
312
|
+
#
|
313
|
+
# @return [OpenSSL::X509::Store]
|
314
|
+
#
|
315
|
+
def self.default_ssl_cert_store
|
316
|
+
cert_store = OpenSSL::X509::Store.new
|
317
|
+
cert_store.set_default_paths
|
318
|
+
|
319
|
+
# set_default_paths() doesn't do anything on Windows, so look up
|
320
|
+
# certificates using the win32 API.
|
321
|
+
if RestClient::Platform.windows?
|
322
|
+
RestClient::Windows::RootCerts.instance.to_a.uniq.each do |cert|
|
323
|
+
cert_store.add_cert(cert)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
cert_store
|
328
|
+
end
|
329
|
+
|
143
330
|
def print_verify_callback_warnings
|
144
331
|
warned = false
|
145
332
|
if RestClient::Platform.mac?
|
@@ -159,27 +346,20 @@ module RestClient
|
|
159
346
|
|
160
347
|
net = net_http_class.new(uri.host, uri.port)
|
161
348
|
net.use_ssl = uri.is_a?(URI::HTTPS)
|
162
|
-
if
|
163
|
-
|
164
|
-
net.verify_mode = @verify_ssl
|
165
|
-
else
|
166
|
-
net.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
167
|
-
end
|
168
|
-
else
|
169
|
-
net.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
170
|
-
end
|
171
|
-
net.cert = @ssl_client_cert if @ssl_client_cert
|
172
|
-
net.key = @ssl_client_key if @ssl_client_key
|
173
|
-
net.ca_file = @ssl_ca_file if @ssl_ca_file
|
174
|
-
net.read_timeout = @timeout if @timeout
|
175
|
-
net.open_timeout = @open_timeout if @open_timeout
|
349
|
+
net.ssl_version = ssl_version if ssl_version
|
350
|
+
net.ciphers = ssl_ciphers if ssl_ciphers
|
176
351
|
|
177
|
-
|
178
|
-
net.read_timeout = nil if @timeout == -1
|
179
|
-
net.open_timeout = nil if @open_timeout == -1
|
352
|
+
net.verify_mode = verify_ssl
|
180
353
|
|
181
|
-
|
182
|
-
|
354
|
+
net.cert = ssl_client_cert if ssl_client_cert
|
355
|
+
net.key = ssl_client_key if ssl_client_key
|
356
|
+
net.ca_file = ssl_ca_file if ssl_ca_file
|
357
|
+
net.ca_path = ssl_ca_path if ssl_ca_path
|
358
|
+
net.cert_store = ssl_cert_store if ssl_cert_store
|
359
|
+
|
360
|
+
# We no longer rely on net.verify_callback for the main SSL verification
|
361
|
+
# because it's not well supported on all platforms (see comments below).
|
362
|
+
# But do allow users to set one if they want.
|
183
363
|
if ssl_verify_callback
|
184
364
|
net.verify_callback = ssl_verify_callback
|
185
365
|
|
@@ -197,30 +377,70 @@ module RestClient
|
|
197
377
|
end
|
198
378
|
end
|
199
379
|
|
380
|
+
if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE
|
381
|
+
warn('WARNING: OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE')
|
382
|
+
warn('This dangerous monkey patch leaves you open to MITM attacks!')
|
383
|
+
warn('Try passing :verify_ssl => false instead.')
|
384
|
+
end
|
385
|
+
|
386
|
+
if defined? @timeout
|
387
|
+
if @timeout == -1
|
388
|
+
warn 'To disable read timeouts, please set timeout to nil instead of -1'
|
389
|
+
@timeout = nil
|
390
|
+
end
|
391
|
+
net.read_timeout = @timeout
|
392
|
+
end
|
393
|
+
if defined? @open_timeout
|
394
|
+
if @open_timeout == -1
|
395
|
+
warn 'To disable open timeouts, please set open_timeout to nil instead of -1'
|
396
|
+
@open_timeout = nil
|
397
|
+
end
|
398
|
+
net.open_timeout = @open_timeout
|
399
|
+
end
|
400
|
+
|
200
401
|
RestClient.before_execution_procs.each do |before_proc|
|
201
402
|
before_proc.call(req, args)
|
202
403
|
end
|
203
404
|
|
204
405
|
log_request
|
205
406
|
|
407
|
+
|
206
408
|
net.start do |http|
|
207
409
|
if @block_response
|
208
|
-
http
|
410
|
+
net_http_do_request(http, req, payload ? payload.to_s : nil,
|
411
|
+
& @block_response)
|
209
412
|
else
|
210
|
-
res = http
|
413
|
+
res = net_http_do_request(http, req, payload ? payload.to_s : nil) \
|
414
|
+
{ |http_response| fetch_body(http_response) }
|
211
415
|
log_response res
|
212
416
|
process_result res, & block
|
213
417
|
end
|
214
418
|
end
|
215
419
|
rescue EOFError
|
216
420
|
raise RestClient::ServerBrokeConnection
|
217
|
-
rescue Timeout::Error
|
421
|
+
rescue Timeout::Error, Errno::ETIMEDOUT
|
218
422
|
raise RestClient::RequestTimeout
|
423
|
+
|
219
424
|
rescue OpenSSL::SSL::SSLError => error
|
220
|
-
#
|
221
|
-
#
|
222
|
-
|
223
|
-
|
425
|
+
# TODO: deprecate and remove RestClient::SSLCertificateNotVerified and just
|
426
|
+
# pass through OpenSSL::SSL::SSLError directly.
|
427
|
+
#
|
428
|
+
# Exceptions in verify_callback are ignored [1], and jruby doesn't support
|
429
|
+
# it at all [2]. RestClient has to catch OpenSSL::SSL::SSLError and either
|
430
|
+
# re-throw it as is, or throw SSLCertificateNotVerified based on the
|
431
|
+
# contents of the message field of the original exception.
|
432
|
+
#
|
433
|
+
# The client has to handle OpenSSL::SSL::SSLError exceptions anyway, so
|
434
|
+
# we shouldn't make them handle both OpenSSL and RestClient exceptions.
|
435
|
+
#
|
436
|
+
# [1] https://github.com/ruby/ruby/blob/89e70fe8e7/ext/openssl/ossl.c#L238
|
437
|
+
# [2] https://github.com/jruby/jruby/issues/597
|
438
|
+
|
439
|
+
if error.message.include?("certificate verify failed")
|
440
|
+
raise SSLCertificateNotVerified.new(error.message)
|
441
|
+
else
|
442
|
+
raise error
|
443
|
+
end
|
224
444
|
end
|
225
445
|
|
226
446
|
def setup_credentials(req)
|
@@ -233,17 +453,18 @@ module RestClient
|
|
233
453
|
# Stolen from http://www.ruby-forum.com/topic/166423
|
234
454
|
# Kudos to _why!
|
235
455
|
@tf = Tempfile.new("rest-client")
|
456
|
+
@tf.binmode
|
236
457
|
size, total = 0, http_response.header['Content-Length'].to_i
|
237
458
|
http_response.read_body do |chunk|
|
238
459
|
@tf.write chunk
|
239
460
|
size += chunk.size
|
240
461
|
if RestClient.log
|
241
462
|
if size == 0
|
242
|
-
RestClient.log << "
|
463
|
+
RestClient.log << "%s %s done (0 length file\n)" % [@method, @url]
|
243
464
|
elsif total == 0
|
244
|
-
RestClient.log << "
|
465
|
+
RestClient.log << "%s %s (zero content length)\n" % [@method, @url]
|
245
466
|
else
|
246
|
-
RestClient.log << "
|
467
|
+
RestClient.log << "%s %s %d%% done (%d of %d)\n" % [@method, @url, (size * 100) / total, size, total]
|
247
468
|
end
|
248
469
|
end
|
249
470
|
end
|
@@ -335,9 +556,10 @@ module RestClient
|
|
335
556
|
end
|
336
557
|
|
337
558
|
private
|
338
|
-
|
339
|
-
|
340
|
-
|
559
|
+
|
560
|
+
def parser
|
561
|
+
URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
562
|
+
end
|
341
563
|
|
342
564
|
end
|
343
565
|
end
|