rest-client 1.6.7 → 1.8.0
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.
- checksums.yaml +7 -0
 - data/.gitignore +7 -0
 - data/.rspec +1 -0
 - data/.travis.yml +14 -0
 - data/AUTHORS +81 -0
 - data/Gemfile +11 -0
 - data/LICENSE +21 -0
 - data/README.rdoc +63 -24
 - data/Rakefile +85 -35
 - data/bin/restclient +9 -8
 - data/history.md +63 -1
 - data/lib/restclient/abstract_response.rb +44 -15
 - data/lib/restclient/exceptions.rb +20 -10
 - data/lib/restclient/payload.rb +21 -18
 - data/lib/restclient/platform.rb +30 -0
 - data/lib/restclient/raw_response.rb +3 -2
 - data/lib/restclient/request.rb +368 -63
 - data/lib/restclient/resource.rb +3 -4
 - data/lib/restclient/response.rb +2 -5
 - data/lib/restclient/version.rb +7 -0
 - data/lib/restclient/windows/root_certs.rb +105 -0
 - data/lib/restclient/windows.rb +8 -0
 - data/lib/restclient.rb +6 -15
 - data/rest-client.gemspec +30 -0
 - data/rest-client.windows.gemspec +19 -0
 - data/spec/integration/capath_digicert/244b5494.0 +19 -0
 - data/spec/integration/capath_digicert/81b9768f.0 +19 -0
 - data/spec/integration/capath_digicert/README +8 -0
 - data/spec/integration/capath_digicert/digicert.crt +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/certs/digicert.crt +19 -0
 - data/spec/{integration_spec.rb → integration/integration_spec.rb} +10 -13
 - data/spec/integration/request_spec.rb +86 -7
 - data/spec/spec_helper.rb +2 -0
 - data/spec/{abstract_response_spec.rb → unit/abstract_response_spec.rb} +18 -15
 - data/spec/{exceptions_spec.rb → unit/exceptions_spec.rb} +17 -20
 - data/spec/unit/master_shake.jpg +0 -0
 - data/spec/{payload_spec.rb → unit/payload_spec.rb} +42 -31
 - data/spec/unit/raw_response_spec.rb +18 -0
 - data/spec/{request2_spec.rb → unit/request2_spec.rb} +6 -14
 - data/spec/unit/request_spec.rb +917 -0
 - data/spec/{resource_spec.rb → unit/resource_spec.rb} +27 -31
 - data/spec/{response_spec.rb → unit/response_spec.rb} +63 -57
 - data/spec/{restclient_spec.rb → unit/restclient_spec.rb} +8 -2
 - data/spec/unit/windows/root_certs_spec.rb +22 -0
 - metadata +210 -112
 - data/VERSION +0 -1
 - data/lib/restclient/net_http_ext.rb +0 -55
 - data/spec/base.rb +0 -16
 - data/spec/integration/certs/equifax.crt +0 -19
 - data/spec/master_shake.jpg +0 -0
 - data/spec/raw_response_spec.rb +0 -17
 - data/spec/request_spec.rb +0 -529
 
    
        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,20 +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 
     | 
    
         
            -
              # * : 
     | 
| 
      
 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=
         
     | 
| 
       25 
33 
     | 
    
         
             
              class Request
         
     | 
| 
       26 
34 
     | 
    
         | 
| 
       27 
35 
     | 
    
         
             
                attr_reader :method, :url, :headers, :cookies,
         
     | 
| 
       28 
36 
     | 
    
         
             
                            :payload, :user, :password, :timeout, :max_redirects,
         
     | 
| 
       29 
     | 
    
         
            -
                            :open_timeout, :raw_response, : 
     | 
| 
       30 
     | 
    
         
            -
                            : 
     | 
| 
      
 37 
     | 
    
         
            +
                            :open_timeout, :raw_response, :processed_headers, :args,
         
     | 
| 
      
 38 
     | 
    
         
            +
                            :ssl_opts
         
     | 
| 
       31 
39 
     | 
    
         | 
| 
       32 
40 
     | 
    
         
             
                def self.execute(args, & block)
         
     | 
| 
       33 
41 
     | 
    
         
             
                  new(args).execute(& block)
         
     | 
| 
       34 
42 
     | 
    
         
             
                end
         
     | 
| 
       35 
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 
     | 
    
         
            +
             
     | 
| 
       36 
105 
     | 
    
         
             
                def initialize args
         
     | 
| 
       37 
106 
     | 
    
         
             
                  @method = args[:method] or raise ArgumentError, "must pass :method"
         
     | 
| 
       38 
107 
     | 
    
         
             
                  @headers = args[:headers] || {}
         
     | 
| 
         @@ -45,14 +114,57 @@ module RestClient 
     | 
|
| 
       45 
114 
     | 
    
         
             
                  @payload = Payload.generate(args[:payload])
         
     | 
| 
       46 
115 
     | 
    
         
             
                  @user = args[:user]
         
     | 
| 
       47 
116 
     | 
    
         
             
                  @password = args[:password]
         
     | 
| 
       48 
     | 
    
         
            -
                   
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 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
         
     | 
| 
       50 
123 
     | 
    
         
             
                  @block_response = args[:block_response]
         
     | 
| 
       51 
124 
     | 
    
         
             
                  @raw_response = args[:raw_response] || false
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                  @ 
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
                   
     | 
| 
      
 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 
     | 
    
         
            +
             
     | 
| 
       56 
168 
     | 
    
         
             
                  @tf = nil # If you are a raw request, this is your tempfile
         
     | 
| 
       57 
169 
     | 
    
         
             
                  @max_redirects = args[:max_redirects] || 10
         
     | 
| 
       58 
170 
     | 
    
         
             
                  @processed_headers = make_headers headers
         
     | 
| 
         @@ -66,6 +178,16 @@ module RestClient 
     | 
|
| 
       66 
178 
     | 
    
         
             
                  payload.close if payload
         
     | 
| 
       67 
179 
     | 
    
         
             
                end
         
     | 
| 
       68 
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 
     | 
    
         
            +
             
     | 
| 
       69 
191 
     | 
    
         
             
                # Extract the query parameters and append them to the url
         
     | 
| 
       70 
192 
     | 
    
         
             
                def process_url_params url, headers
         
     | 
| 
       71 
193 
     | 
    
         
             
                  url_params = {}
         
     | 
| 
         @@ -87,13 +209,46 @@ module RestClient 
     | 
|
| 
       87 
209 
     | 
    
         | 
| 
       88 
210 
     | 
    
         
             
                def make_headers user_headers
         
     | 
| 
       89 
211 
     | 
    
         
             
                  unless @cookies.empty?
         
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
      
 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('; ')
         
     | 
| 
       91 
226 
     | 
    
         
             
                  end
         
     | 
| 
       92 
227 
     | 
    
         
             
                  headers = stringify_headers(default_headers).merge(stringify_headers(user_headers))
         
     | 
| 
       93 
228 
     | 
    
         
             
                  headers.merge!(@payload.headers) if @payload
         
     | 
| 
       94 
229 
     | 
    
         
             
                  headers
         
     | 
| 
       95 
230 
     | 
    
         
             
                end
         
     | 
| 
       96 
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 
     | 
    
         
            +
             
     | 
| 
       97 
252 
     | 
    
         
             
                def net_http_class
         
     | 
| 
       98 
253 
     | 
    
         
             
                  if RestClient.proxy
         
     | 
| 
       99 
254 
     | 
    
         
             
                    proxy_uri = URI.parse(RestClient.proxy)
         
     | 
| 
         @@ -107,6 +262,15 @@ module RestClient 
     | 
|
| 
       107 
262 
     | 
    
         
             
                  Net::HTTP.const_get(method.to_s.capitalize)
         
     | 
| 
       108 
263 
     | 
    
         
             
                end
         
     | 
| 
       109 
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 
     | 
    
         
            +
             
     | 
| 
       110 
274 
     | 
    
         
             
                def parse_url(url)
         
     | 
| 
       111 
275 
     | 
    
         
             
                  url = "http://#{url}" unless url.match(/^http/)
         
     | 
| 
       112 
276 
     | 
    
         
             
                  URI.parse(url)
         
     | 
| 
         @@ -116,6 +280,9 @@ module RestClient 
     | 
|
| 
       116 
280 
     | 
    
         
             
                  uri = parse_url(url)
         
     | 
| 
       117 
281 
     | 
    
         
             
                  @user = CGI.unescape(uri.user) if uri.user
         
     | 
| 
       118 
282 
     | 
    
         
             
                  @password = CGI.unescape(uri.password) if uri.password
         
     | 
| 
      
 283 
     | 
    
         
            +
                  if !@user && !@password
         
     | 
| 
      
 284 
     | 
    
         
            +
                    @user, @password = Netrc.read[uri.host]
         
     | 
| 
      
 285 
     | 
    
         
            +
                  end
         
     | 
| 
       119 
286 
     | 
    
         
             
                  uri
         
     | 
| 
       120 
287 
     | 
    
         
             
                end
         
     | 
| 
       121 
288 
     | 
    
         | 
| 
         @@ -129,39 +296,112 @@ module RestClient 
     | 
|
| 
       129 
296 
     | 
    
         
             
                      if p[k].is_a? Hash
         
     | 
| 
       130 
297 
     | 
    
         
             
                        process_payload(p[k], key)
         
     | 
| 
       131 
298 
     | 
    
         
             
                      else
         
     | 
| 
       132 
     | 
    
         
            -
                        value =  
     | 
| 
      
 299 
     | 
    
         
            +
                        value = parser.escape(p[k].to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
         
     | 
| 
       133 
300 
     | 
    
         
             
                        "#{key}=#{value}"
         
     | 
| 
       134 
301 
     | 
    
         
             
                      end
         
     | 
| 
       135 
302 
     | 
    
         
             
                    end.join("&")
         
     | 
| 
       136 
303 
     | 
    
         
             
                  end
         
     | 
| 
       137 
304 
     | 
    
         
             
                end
         
     | 
| 
       138 
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 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 324 
     | 
    
         
            +
                        cert_store.add_cert(cert)
         
     | 
| 
      
 325 
     | 
    
         
            +
                      rescue OpenSSL::X509::StoreError => err
         
     | 
| 
      
 326 
     | 
    
         
            +
                        # ignore duplicate certs
         
     | 
| 
      
 327 
     | 
    
         
            +
                        raise unless err.message == 'cert already in hash table'
         
     | 
| 
      
 328 
     | 
    
         
            +
                      end
         
     | 
| 
      
 329 
     | 
    
         
            +
                    end
         
     | 
| 
      
 330 
     | 
    
         
            +
                  end
         
     | 
| 
      
 331 
     | 
    
         
            +
             
     | 
| 
      
 332 
     | 
    
         
            +
                  cert_store
         
     | 
| 
      
 333 
     | 
    
         
            +
                end
         
     | 
| 
      
 334 
     | 
    
         
            +
             
     | 
| 
      
 335 
     | 
    
         
            +
                def print_verify_callback_warnings
         
     | 
| 
      
 336 
     | 
    
         
            +
                  warned = false
         
     | 
| 
      
 337 
     | 
    
         
            +
                  if RestClient::Platform.mac_mri?
         
     | 
| 
      
 338 
     | 
    
         
            +
                    warn('warning: ssl_verify_callback return code is ignored on OS X')
         
     | 
| 
      
 339 
     | 
    
         
            +
                    warned = true
         
     | 
| 
      
 340 
     | 
    
         
            +
                  end
         
     | 
| 
      
 341 
     | 
    
         
            +
                  if RestClient::Platform.jruby?
         
     | 
| 
      
 342 
     | 
    
         
            +
                    warn('warning: SSL verify_callback may not work correctly in jruby')
         
     | 
| 
      
 343 
     | 
    
         
            +
                    warn('see https://github.com/jruby/jruby/issues/597')
         
     | 
| 
      
 344 
     | 
    
         
            +
                    warned = true
         
     | 
| 
      
 345 
     | 
    
         
            +
                  end
         
     | 
| 
      
 346 
     | 
    
         
            +
                  warned
         
     | 
| 
      
 347 
     | 
    
         
            +
                end
         
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
       139 
349 
     | 
    
         
             
                def transmit uri, req, payload, & block
         
     | 
| 
       140 
350 
     | 
    
         
             
                  setup_credentials req
         
     | 
| 
       141 
351 
     | 
    
         | 
| 
       142 
352 
     | 
    
         
             
                  net = net_http_class.new(uri.host, uri.port)
         
     | 
| 
       143 
353 
     | 
    
         
             
                  net.use_ssl = uri.is_a?(URI::HTTPS)
         
     | 
| 
       144 
     | 
    
         
            -
                   
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
      
 354 
     | 
    
         
            +
                  net.ssl_version = ssl_version if ssl_version
         
     | 
| 
      
 355 
     | 
    
         
            +
                  net.ciphers = ssl_ciphers if ssl_ciphers
         
     | 
| 
      
 356 
     | 
    
         
            +
             
     | 
| 
      
 357 
     | 
    
         
            +
                  net.verify_mode = verify_ssl
         
     | 
| 
      
 358 
     | 
    
         
            +
             
     | 
| 
      
 359 
     | 
    
         
            +
                  net.cert = ssl_client_cert if ssl_client_cert
         
     | 
| 
      
 360 
     | 
    
         
            +
                  net.key = ssl_client_key if ssl_client_key
         
     | 
| 
      
 361 
     | 
    
         
            +
                  net.ca_file = ssl_ca_file if ssl_ca_file
         
     | 
| 
      
 362 
     | 
    
         
            +
                  net.ca_path = ssl_ca_path if ssl_ca_path
         
     | 
| 
      
 363 
     | 
    
         
            +
                  net.cert_store = ssl_cert_store if ssl_cert_store
         
     | 
| 
      
 364 
     | 
    
         
            +
             
     | 
| 
      
 365 
     | 
    
         
            +
                  # We no longer rely on net.verify_callback for the main SSL verification
         
     | 
| 
      
 366 
     | 
    
         
            +
                  # because it's not well supported on all platforms (see comments below).
         
     | 
| 
      
 367 
     | 
    
         
            +
                  # But do allow users to set one if they want.
         
     | 
| 
      
 368 
     | 
    
         
            +
                  if ssl_verify_callback
         
     | 
| 
      
 369 
     | 
    
         
            +
                    net.verify_callback = ssl_verify_callback
         
     | 
| 
      
 370 
     | 
    
         
            +
             
     | 
| 
      
 371 
     | 
    
         
            +
                    # Hilariously, jruby only calls the callback when cert_store is set to
         
     | 
| 
      
 372 
     | 
    
         
            +
                    # something, so make sure to set one.
         
     | 
| 
      
 373 
     | 
    
         
            +
                    # https://github.com/jruby/jruby/issues/597
         
     | 
| 
      
 374 
     | 
    
         
            +
                    if RestClient::Platform.jruby?
         
     | 
| 
      
 375 
     | 
    
         
            +
                      net.cert_store ||= OpenSSL::X509::Store.new
         
     | 
| 
      
 376 
     | 
    
         
            +
                    end
         
     | 
| 
      
 377 
     | 
    
         
            +
             
     | 
| 
      
 378 
     | 
    
         
            +
                    if ssl_verify_callback_warnings != false
         
     | 
| 
      
 379 
     | 
    
         
            +
                      if print_verify_callback_warnings
         
     | 
| 
      
 380 
     | 
    
         
            +
                        warn('pass :ssl_verify_callback_warnings => false to silence this')
         
     | 
| 
       152 
381 
     | 
    
         
             
                      end
         
     | 
| 
       153 
     | 
    
         
            -
                      true
         
     | 
| 
       154 
382 
     | 
    
         
             
                    end
         
     | 
| 
       155 
383 
     | 
    
         
             
                  end
         
     | 
| 
       156 
     | 
    
         
            -
                  net.cert = @ssl_client_cert if @ssl_client_cert
         
     | 
| 
       157 
     | 
    
         
            -
                  net.key = @ssl_client_key if @ssl_client_key
         
     | 
| 
       158 
     | 
    
         
            -
                  net.ca_file = @ssl_ca_file if @ssl_ca_file
         
     | 
| 
       159 
     | 
    
         
            -
                  net.read_timeout = @timeout if @timeout
         
     | 
| 
       160 
     | 
    
         
            -
                  net.open_timeout = @open_timeout if @open_timeout
         
     | 
| 
       161 
384 
     | 
    
         | 
| 
       162 
     | 
    
         
            -
                   
     | 
| 
       163 
     | 
    
         
            -
             
     | 
| 
       164 
     | 
    
         
            -
             
     | 
| 
      
 385 
     | 
    
         
            +
                  if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE
         
     | 
| 
      
 386 
     | 
    
         
            +
                    warn('WARNING: OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE')
         
     | 
| 
      
 387 
     | 
    
         
            +
                    warn('This dangerous monkey patch leaves you open to MITM attacks!')
         
     | 
| 
      
 388 
     | 
    
         
            +
                    warn('Try passing :verify_ssl => false instead.')
         
     | 
| 
      
 389 
     | 
    
         
            +
                  end
         
     | 
| 
      
 390 
     | 
    
         
            +
             
     | 
| 
      
 391 
     | 
    
         
            +
                  if defined? @timeout
         
     | 
| 
      
 392 
     | 
    
         
            +
                    if @timeout == -1
         
     | 
| 
      
 393 
     | 
    
         
            +
                      warn 'To disable read timeouts, please set timeout to nil instead of -1'
         
     | 
| 
      
 394 
     | 
    
         
            +
                      @timeout = nil
         
     | 
| 
      
 395 
     | 
    
         
            +
                    end
         
     | 
| 
      
 396 
     | 
    
         
            +
                    net.read_timeout = @timeout
         
     | 
| 
      
 397 
     | 
    
         
            +
                  end
         
     | 
| 
      
 398 
     | 
    
         
            +
                  if defined? @open_timeout
         
     | 
| 
      
 399 
     | 
    
         
            +
                    if @open_timeout == -1
         
     | 
| 
      
 400 
     | 
    
         
            +
                      warn 'To disable open timeouts, please set open_timeout to nil instead of -1'
         
     | 
| 
      
 401 
     | 
    
         
            +
                      @open_timeout = nil
         
     | 
| 
      
 402 
     | 
    
         
            +
                    end
         
     | 
| 
      
 403 
     | 
    
         
            +
                    net.open_timeout = @open_timeout
         
     | 
| 
      
 404 
     | 
    
         
            +
                  end
         
     | 
| 
       165 
405 
     | 
    
         | 
| 
       166 
406 
     | 
    
         
             
                  RestClient.before_execution_procs.each do |before_proc|
         
     | 
| 
       167 
407 
     | 
    
         
             
                    before_proc.call(req, args)
         
     | 
| 
         @@ -169,19 +409,43 @@ module RestClient 
     | 
|
| 
       169 
409 
     | 
    
         | 
| 
       170 
410 
     | 
    
         
             
                  log_request
         
     | 
| 
       171 
411 
     | 
    
         | 
| 
      
 412 
     | 
    
         
            +
             
     | 
| 
       172 
413 
     | 
    
         
             
                  net.start do |http|
         
     | 
| 
       173 
414 
     | 
    
         
             
                    if @block_response
         
     | 
| 
       174 
     | 
    
         
            -
                      http 
     | 
| 
      
 415 
     | 
    
         
            +
                      net_http_do_request(http, req, payload ? payload.to_s : nil,
         
     | 
| 
      
 416 
     | 
    
         
            +
                                          &@block_response)
         
     | 
| 
       175 
417 
     | 
    
         
             
                    else
         
     | 
| 
       176 
     | 
    
         
            -
                      res = http 
     | 
| 
      
 418 
     | 
    
         
            +
                      res = net_http_do_request(http, req, payload ? payload.to_s : nil) \
         
     | 
| 
      
 419 
     | 
    
         
            +
                        { |http_response| fetch_body(http_response) }
         
     | 
| 
       177 
420 
     | 
    
         
             
                      log_response res
         
     | 
| 
       178 
421 
     | 
    
         
             
                      process_result res, & block
         
     | 
| 
       179 
422 
     | 
    
         
             
                    end
         
     | 
| 
       180 
423 
     | 
    
         
             
                  end
         
     | 
| 
       181 
424 
     | 
    
         
             
                rescue EOFError
         
     | 
| 
       182 
425 
     | 
    
         
             
                  raise RestClient::ServerBrokeConnection
         
     | 
| 
       183 
     | 
    
         
            -
                rescue Timeout::Error
         
     | 
| 
      
 426 
     | 
    
         
            +
                rescue Timeout::Error, Errno::ETIMEDOUT
         
     | 
| 
       184 
427 
     | 
    
         
             
                  raise RestClient::RequestTimeout
         
     | 
| 
      
 428 
     | 
    
         
            +
             
     | 
| 
      
 429 
     | 
    
         
            +
                rescue OpenSSL::SSL::SSLError => error
         
     | 
| 
      
 430 
     | 
    
         
            +
                  # TODO: deprecate and remove RestClient::SSLCertificateNotVerified and just
         
     | 
| 
      
 431 
     | 
    
         
            +
                  # pass through OpenSSL::SSL::SSLError directly.
         
     | 
| 
      
 432 
     | 
    
         
            +
                  #
         
     | 
| 
      
 433 
     | 
    
         
            +
                  # Exceptions in verify_callback are ignored [1], and jruby doesn't support
         
     | 
| 
      
 434 
     | 
    
         
            +
                  # it at all [2]. RestClient has to catch OpenSSL::SSL::SSLError and either
         
     | 
| 
      
 435 
     | 
    
         
            +
                  # re-throw it as is, or throw SSLCertificateNotVerified based on the
         
     | 
| 
      
 436 
     | 
    
         
            +
                  # contents of the message field of the original exception.
         
     | 
| 
      
 437 
     | 
    
         
            +
                  #
         
     | 
| 
      
 438 
     | 
    
         
            +
                  # The client has to handle OpenSSL::SSL::SSLError exceptions anyway, so
         
     | 
| 
      
 439 
     | 
    
         
            +
                  # we shouldn't make them handle both OpenSSL and RestClient exceptions.
         
     | 
| 
      
 440 
     | 
    
         
            +
                  #
         
     | 
| 
      
 441 
     | 
    
         
            +
                  # [1] https://github.com/ruby/ruby/blob/89e70fe8e7/ext/openssl/ossl.c#L238
         
     | 
| 
      
 442 
     | 
    
         
            +
                  # [2] https://github.com/jruby/jruby/issues/597
         
     | 
| 
      
 443 
     | 
    
         
            +
             
     | 
| 
      
 444 
     | 
    
         
            +
                  if error.message.include?("certificate verify failed")
         
     | 
| 
      
 445 
     | 
    
         
            +
                    raise SSLCertificateNotVerified.new(error.message)
         
     | 
| 
      
 446 
     | 
    
         
            +
                  else
         
     | 
| 
      
 447 
     | 
    
         
            +
                    raise error
         
     | 
| 
      
 448 
     | 
    
         
            +
                  end
         
     | 
| 
       185 
449 
     | 
    
         
             
                end
         
     | 
| 
       186 
450 
     | 
    
         | 
| 
       187 
451 
     | 
    
         
             
                def setup_credentials(req)
         
     | 
| 
         @@ -194,17 +458,18 @@ module RestClient 
     | 
|
| 
       194 
458 
     | 
    
         
             
                    # Stolen from http://www.ruby-forum.com/topic/166423
         
     | 
| 
       195 
459 
     | 
    
         
             
                    # Kudos to _why!
         
     | 
| 
       196 
460 
     | 
    
         
             
                    @tf = Tempfile.new("rest-client")
         
     | 
| 
      
 461 
     | 
    
         
            +
                    @tf.binmode
         
     | 
| 
       197 
462 
     | 
    
         
             
                    size, total = 0, http_response.header['Content-Length'].to_i
         
     | 
| 
       198 
463 
     | 
    
         
             
                    http_response.read_body do |chunk|
         
     | 
| 
       199 
464 
     | 
    
         
             
                      @tf.write chunk
         
     | 
| 
       200 
465 
     | 
    
         
             
                      size += chunk.size
         
     | 
| 
       201 
466 
     | 
    
         
             
                      if RestClient.log
         
     | 
| 
       202 
467 
     | 
    
         
             
                        if size == 0
         
     | 
| 
       203 
     | 
    
         
            -
                          RestClient.log << " 
     | 
| 
      
 468 
     | 
    
         
            +
                          RestClient.log << "%s %s done (0 length file)\n" % [@method, @url]
         
     | 
| 
       204 
469 
     | 
    
         
             
                        elsif total == 0
         
     | 
| 
       205 
     | 
    
         
            -
                          RestClient.log << " 
     | 
| 
      
 470 
     | 
    
         
            +
                          RestClient.log << "%s %s (zero content length)\n" % [@method, @url]
         
     | 
| 
       206 
471 
     | 
    
         
             
                        else
         
     | 
| 
       207 
     | 
    
         
            -
                          RestClient.log << " 
     | 
| 
      
 472 
     | 
    
         
            +
                          RestClient.log << "%s %s %d%% done (%d of %d)\n" % [@method, @url, (size * 100) / total, size, total]
         
     | 
| 
       208 
473 
     | 
    
         
             
                        end
         
     | 
| 
       209 
474 
     | 
    
         
             
                      end
         
     | 
| 
       210 
475 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -219,9 +484,9 @@ module RestClient 
     | 
|
| 
       219 
484 
     | 
    
         
             
                def process_result res, & block
         
     | 
| 
       220 
485 
     | 
    
         
             
                  if @raw_response
         
     | 
| 
       221 
486 
     | 
    
         
             
                    # We don't decode raw requests
         
     | 
| 
       222 
     | 
    
         
            -
                    response = RawResponse.new(@tf, res, args)
         
     | 
| 
      
 487 
     | 
    
         
            +
                    response = RawResponse.new(@tf, res, args, self)
         
     | 
| 
       223 
488 
     | 
    
         
             
                  else
         
     | 
| 
       224 
     | 
    
         
            -
                    response = Response.create(Request.decode(res['content-encoding'], res.body), res, args)
         
     | 
| 
      
 489 
     | 
    
         
            +
                    response = Response.create(Request.decode(res['content-encoding'], res.body), res, args, self)
         
     | 
| 
       225 
490 
     | 
    
         
             
                  end
         
     | 
| 
       226 
491 
     | 
    
         | 
| 
       227 
492 
     | 
    
         
             
                  if block_given?
         
     | 
| 
         @@ -251,20 +516,36 @@ module RestClient 
     | 
|
| 
       251 
516 
     | 
    
         
             
                end
         
     | 
| 
       252 
517 
     | 
    
         | 
| 
       253 
518 
     | 
    
         
             
                def log_request
         
     | 
| 
       254 
     | 
    
         
            -
                   
     | 
| 
       255 
     | 
    
         
            -
             
     | 
| 
       256 
     | 
    
         
            -
             
     | 
| 
       257 
     | 
    
         
            -
             
     | 
| 
       258 
     | 
    
         
            -
                     
     | 
| 
       259 
     | 
    
         
            -
                     
     | 
| 
      
 519 
     | 
    
         
            +
                  return unless RestClient.log
         
     | 
| 
      
 520 
     | 
    
         
            +
             
     | 
| 
      
 521 
     | 
    
         
            +
                  out = []
         
     | 
| 
      
 522 
     | 
    
         
            +
                  sanitized_url = begin
         
     | 
| 
      
 523 
     | 
    
         
            +
                    uri = URI.parse(url)
         
     | 
| 
      
 524 
     | 
    
         
            +
                    uri.password = "REDACTED" if uri.password
         
     | 
| 
      
 525 
     | 
    
         
            +
                    uri.to_s
         
     | 
| 
      
 526 
     | 
    
         
            +
                  rescue URI::InvalidURIError
         
     | 
| 
      
 527 
     | 
    
         
            +
                    # An attacker may be able to manipulate the URL to be
         
     | 
| 
      
 528 
     | 
    
         
            +
                    # invalid, which could force discloure of a password if
         
     | 
| 
      
 529 
     | 
    
         
            +
                    # we show any of the un-parsed URL here.
         
     | 
| 
      
 530 
     | 
    
         
            +
                    "[invalid uri]"
         
     | 
| 
       260 
531 
     | 
    
         
             
                  end
         
     | 
| 
      
 532 
     | 
    
         
            +
             
     | 
| 
      
 533 
     | 
    
         
            +
                  out << "RestClient.#{method} #{sanitized_url.inspect}"
         
     | 
| 
      
 534 
     | 
    
         
            +
                  out << payload.short_inspect if payload
         
     | 
| 
      
 535 
     | 
    
         
            +
                  out << processed_headers.to_a.sort.map { |(k, v)| [k.inspect, v.inspect].join("=>") }.join(", ")
         
     | 
| 
      
 536 
     | 
    
         
            +
                  RestClient.log << out.join(', ') + "\n"
         
     | 
| 
       261 
537 
     | 
    
         
             
                end
         
     | 
| 
       262 
538 
     | 
    
         | 
| 
       263 
539 
     | 
    
         
             
                def log_response res
         
     | 
| 
       264 
     | 
    
         
            -
                   
     | 
| 
       265 
     | 
    
         
            -
             
     | 
| 
       266 
     | 
    
         
            -
             
     | 
| 
       267 
     | 
    
         
            -
             
     | 
| 
      
 540 
     | 
    
         
            +
                  return unless RestClient.log
         
     | 
| 
      
 541 
     | 
    
         
            +
             
     | 
| 
      
 542 
     | 
    
         
            +
                  size = if @raw_response
         
     | 
| 
      
 543 
     | 
    
         
            +
                           File.size(@tf.path)
         
     | 
| 
      
 544 
     | 
    
         
            +
                         else
         
     | 
| 
      
 545 
     | 
    
         
            +
                           res.body.nil? ? 0 : res.body.size
         
     | 
| 
      
 546 
     | 
    
         
            +
                         end
         
     | 
| 
      
 547 
     | 
    
         
            +
             
     | 
| 
      
 548 
     | 
    
         
            +
                  RestClient.log << "# => #{res.code} #{res.class.to_s.gsub(/^Net::HTTP/, '')} | #{(res['Content-type'] || '').gsub(/;.*$/, '')} #{size} bytes\n"
         
     | 
| 
       268 
549 
     | 
    
         
             
                end
         
     | 
| 
       269 
550 
     | 
    
         | 
| 
       270 
551 
     | 
    
         
             
                # Return a hash of headers whose keys are capitalized strings
         
     | 
| 
         @@ -274,8 +555,7 @@ module RestClient 
     | 
|
| 
       274 
555 
     | 
    
         
             
                      key = key.to_s.split(/_/).map { |w| w.capitalize }.join('-')
         
     | 
| 
       275 
556 
     | 
    
         
             
                    end
         
     | 
| 
       276 
557 
     | 
    
         
             
                    if 'CONTENT-TYPE' == key.upcase
         
     | 
| 
       277 
     | 
    
         
            -
                       
     | 
| 
       278 
     | 
    
         
            -
                      result[key] = MIME::Types.type_for_extension target_value
         
     | 
| 
      
 558 
     | 
    
         
            +
                      result[key] = maybe_convert_extension(value.to_s)
         
     | 
| 
       279 
559 
     | 
    
         
             
                    elsif 'ACCEPT' == key.upcase
         
     | 
| 
       280 
560 
     | 
    
         
             
                      # Accept can be composed of several comma-separated values
         
     | 
| 
       281 
561 
     | 
    
         
             
                      if value.is_a? Array
         
     | 
| 
         @@ -283,7 +563,9 @@ module RestClient 
     | 
|
| 
       283 
563 
     | 
    
         
             
                      else
         
     | 
| 
       284 
564 
     | 
    
         
             
                        target_values = value.to_s.split ','
         
     | 
| 
       285 
565 
     | 
    
         
             
                      end
         
     | 
| 
       286 
     | 
    
         
            -
                      result[key] = target_values.map { |ext| 
     | 
| 
      
 566 
     | 
    
         
            +
                      result[key] = target_values.map { |ext|
         
     | 
| 
      
 567 
     | 
    
         
            +
                        maybe_convert_extension(ext.to_s.strip)
         
     | 
| 
      
 568 
     | 
    
         
            +
                      }.join(', ')
         
     | 
| 
       287 
569 
     | 
    
         
             
                    else
         
     | 
| 
       288 
570 
     | 
    
         
             
                      result[key] = value.to_s
         
     | 
| 
       289 
571 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -295,21 +577,44 @@ module RestClient 
     | 
|
| 
       295 
577 
     | 
    
         
             
                  {:accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate'}
         
     | 
| 
       296 
578 
     | 
    
         
             
                end
         
     | 
| 
       297 
579 
     | 
    
         | 
| 
       298 
     | 
    
         
            -
             
     | 
| 
       299 
     | 
    
         
            -
            end
         
     | 
| 
       300 
     | 
    
         
            -
             
     | 
| 
       301 
     | 
    
         
            -
            module MIME
         
     | 
| 
       302 
     | 
    
         
            -
              class Types
         
     | 
| 
      
 580 
     | 
    
         
            +
                private
         
     | 
| 
       303 
581 
     | 
    
         | 
| 
       304 
     | 
    
         
            -
                 
     | 
| 
       305 
     | 
    
         
            -
             
     | 
| 
       306 
     | 
    
         
            -
                  candidates = @extension_index[ext]
         
     | 
| 
       307 
     | 
    
         
            -
                  candidates.empty? ? ext : candidates[0].content_type
         
     | 
| 
      
 582 
     | 
    
         
            +
                def parser
         
     | 
| 
      
 583 
     | 
    
         
            +
                  URI.const_defined?(:Parser) ? URI::Parser.new : URI
         
     | 
| 
       308 
584 
     | 
    
         
             
                end
         
     | 
| 
       309 
585 
     | 
    
         | 
| 
       310 
     | 
    
         
            -
                 
     | 
| 
       311 
     | 
    
         
            -
             
     | 
| 
       312 
     | 
    
         
            -
             
     | 
| 
      
 586 
     | 
    
         
            +
                # Given a MIME type or file extension, return either a MIME type or, if
         
     | 
| 
      
 587 
     | 
    
         
            +
                # none is found, the input unchanged.
         
     | 
| 
      
 588 
     | 
    
         
            +
                #
         
     | 
| 
      
 589 
     | 
    
         
            +
                #     >> maybe_convert_extension('json')
         
     | 
| 
      
 590 
     | 
    
         
            +
                #     => 'application/json'
         
     | 
| 
      
 591 
     | 
    
         
            +
                #
         
     | 
| 
      
 592 
     | 
    
         
            +
                #     >> maybe_convert_extension('unknown')
         
     | 
| 
      
 593 
     | 
    
         
            +
                #     => 'unknown'
         
     | 
| 
      
 594 
     | 
    
         
            +
                #
         
     | 
| 
      
 595 
     | 
    
         
            +
                #     >> maybe_convert_extension('application/xml')
         
     | 
| 
      
 596 
     | 
    
         
            +
                #     => 'application/xml'
         
     | 
| 
      
 597 
     | 
    
         
            +
                #
         
     | 
| 
      
 598 
     | 
    
         
            +
                # @param ext [String]
         
     | 
| 
      
 599 
     | 
    
         
            +
                #
         
     | 
| 
      
 600 
     | 
    
         
            +
                # @return [String]
         
     | 
| 
      
 601 
     | 
    
         
            +
                #
         
     | 
| 
      
 602 
     | 
    
         
            +
                def maybe_convert_extension(ext)
         
     | 
| 
      
 603 
     | 
    
         
            +
                  unless ext =~ /\A[a-zA-Z0-9_@-]+\z/
         
     | 
| 
      
 604 
     | 
    
         
            +
                    # Don't look up strings unless they look like they could be a file
         
     | 
| 
      
 605 
     | 
    
         
            +
                    # extension known to mime-types.
         
     | 
| 
      
 606 
     | 
    
         
            +
                    #
         
     | 
| 
      
 607 
     | 
    
         
            +
                    # There currently isn't any API public way to look up extensions
         
     | 
| 
      
 608 
     | 
    
         
            +
                    # directly out of MIME::Types, but the type_for() method only strips
         
     | 
| 
      
 609 
     | 
    
         
            +
                    # off after a period anyway.
         
     | 
| 
      
 610 
     | 
    
         
            +
                    return ext
         
     | 
| 
      
 611 
     | 
    
         
            +
                  end
         
     | 
| 
      
 612 
     | 
    
         
            +
             
     | 
| 
      
 613 
     | 
    
         
            +
                  types = MIME::Types.type_for(ext)
         
     | 
| 
      
 614 
     | 
    
         
            +
                  if types.empty?
         
     | 
| 
      
 615 
     | 
    
         
            +
                    ext
         
     | 
| 
      
 616 
     | 
    
         
            +
                  else
         
     | 
| 
      
 617 
     | 
    
         
            +
                    types.first.content_type
         
     | 
| 
       313 
618 
     | 
    
         
             
                  end
         
     | 
| 
       314 
619 
     | 
    
         
             
                end
         
     | 
| 
       315 
620 
     | 
    
         
             
              end
         
     | 
    
        data/lib/restclient/resource.rb
    CHANGED
    
    | 
         @@ -149,10 +149,9 @@ module RestClient 
     | 
|
| 
       149 
149 
     | 
    
         
             
                #
         
     | 
| 
       150 
150 
     | 
    
         
             
                def [](suburl, &new_block)
         
     | 
| 
       151 
151 
     | 
    
         
             
                  case
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
                  else
         
     | 
| 
       155 
     | 
    
         
            -
                    self.class.new(concat_urls(url, suburl), options)
         
     | 
| 
      
 152 
     | 
    
         
            +
                  when block_given? then self.class.new(concat_urls(url, suburl), options, &new_block)
         
     | 
| 
      
 153 
     | 
    
         
            +
                  when block        then self.class.new(concat_urls(url, suburl), options, &block)
         
     | 
| 
      
 154 
     | 
    
         
            +
                  else                   self.class.new(concat_urls(url, suburl), options)
         
     | 
| 
       156 
155 
     | 
    
         
             
                  end
         
     | 
| 
       157 
156 
     | 
    
         
             
                end
         
     | 
| 
       158 
157 
     | 
    
         | 
    
        data/lib/restclient/response.rb
    CHANGED
    
    | 
         @@ -6,17 +6,14 @@ module RestClient 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                include AbstractResponse
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                attr_accessor :args, :body, :net_http_res
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
9 
     | 
    
         
             
                def body
         
     | 
| 
       12 
10 
     | 
    
         
             
                  self
         
     | 
| 
       13 
11 
     | 
    
         
             
                end
         
     | 
| 
       14 
12 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                def  
     | 
| 
      
 13 
     | 
    
         
            +
                def self.create body, net_http_res, args, request
         
     | 
| 
       16 
14 
     | 
    
         
             
                  result = body || ''
         
     | 
| 
       17 
15 
     | 
    
         
             
                  result.extend Response
         
     | 
| 
       18 
     | 
    
         
            -
                  result.net_http_res  
     | 
| 
       19 
     | 
    
         
            -
                  result.args = args
         
     | 
| 
      
 16 
     | 
    
         
            +
                  result.response_set_vars(net_http_res, args, request)
         
     | 
| 
       20 
17 
     | 
    
         
             
                  result
         
     | 
| 
       21 
18 
     | 
    
         
             
                end
         
     | 
| 
       22 
19 
     | 
    
         |