httparty 0.15.6 → 0.21.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 +5 -5
- data/.editorconfig +18 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +2 -0
- data/.rubocop_todo.yml +1 -1
- data/Changelog.md +105 -0
- data/Gemfile +7 -0
- data/Guardfile +3 -2
- data/README.md +6 -6
- data/docs/README.md +90 -5
- data/examples/README.md +28 -11
- data/examples/aaws.rb +6 -2
- data/examples/body_stream.rb +14 -0
- data/examples/custom_parsers.rb +4 -0
- data/examples/headers_and_user_agents.rb +7 -3
- data/examples/idn.rb +10 -0
- data/examples/logging.rb +3 -3
- data/examples/microsoft_graph.rb +52 -0
- data/examples/multipart.rb +22 -0
- data/examples/peer_cert.rb +9 -0
- data/examples/stream_download.rb +8 -2
- data/httparty.gemspec +3 -3
- data/lib/httparty/connection_adapter.rb +59 -16
- data/lib/httparty/cookie_hash.rb +10 -8
- data/lib/httparty/decompressor.rb +102 -0
- data/lib/httparty/exceptions.rb +3 -1
- data/lib/httparty/hash_conversions.rb +28 -12
- data/lib/httparty/headers_processor.rb +32 -0
- data/lib/httparty/logger/apache_formatter.rb +31 -6
- data/lib/httparty/logger/curl_formatter.rb +9 -7
- data/lib/httparty/logger/logger.rb +5 -1
- data/lib/httparty/logger/logstash_formatter.rb +61 -0
- data/lib/httparty/module_inheritable_attributes.rb +6 -4
- data/lib/httparty/net_digest_auth.rb +15 -15
- data/lib/httparty/parser.rb +21 -15
- data/lib/httparty/request/body.rb +105 -0
- data/lib/httparty/request/multipart_boundary.rb +13 -0
- data/lib/httparty/request.rb +86 -94
- data/lib/httparty/response/headers.rb +4 -2
- data/lib/httparty/response.rb +59 -8
- data/lib/httparty/response_fragment.rb +21 -0
- data/lib/httparty/text_encoder.rb +72 -0
- data/lib/httparty/utils.rb +13 -0
- data/lib/httparty/version.rb +3 -1
- data/lib/httparty.rb +70 -25
- data/website/css/common.css +1 -1
- metadata +37 -103
- data/.simplecov +0 -1
- data/.travis.yml +0 -9
- data/features/basic_authentication.feature +0 -20
- data/features/command_line.feature +0 -95
- data/features/deals_with_http_error_codes.feature +0 -26
- data/features/digest_authentication.feature +0 -30
- data/features/handles_compressed_responses.feature +0 -27
- data/features/handles_multiple_formats.feature +0 -57
- data/features/steps/env.rb +0 -27
- data/features/steps/httparty_response_steps.rb +0 -56
- data/features/steps/httparty_steps.rb +0 -43
- data/features/steps/mongrel_helper.rb +0 -127
- data/features/steps/remote_service_steps.rb +0 -92
- data/features/supports_read_timeout_option.feature +0 -13
- data/features/supports_redirection.feature +0 -22
- data/features/supports_timeout_option.feature +0 -13
- data/spec/fixtures/delicious.xml +0 -23
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +0 -3
- data/spec/fixtures/ssl/generate.sh +0 -29
- data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
- data/spec/fixtures/ssl/generated/ca.crt +0 -16
- data/spec/fixtures/ssl/generated/ca.key +0 -15
- data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
- data/spec/fixtures/ssl/generated/server.crt +0 -13
- data/spec/fixtures/ssl/generated/server.key +0 -15
- data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
- data/spec/fixtures/twitter.csv +0 -2
- data/spec/fixtures/twitter.json +0 -1
- data/spec/fixtures/twitter.xml +0 -403
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
- data/spec/httparty/connection_adapter_spec.rb +0 -495
- data/spec/httparty/cookie_hash_spec.rb +0 -100
- data/spec/httparty/exception_spec.rb +0 -45
- data/spec/httparty/hash_conversions_spec.rb +0 -49
- data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
- data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
- data/spec/httparty/logger/logger_spec.rb +0 -38
- data/spec/httparty/net_digest_auth_spec.rb +0 -268
- data/spec/httparty/parser_spec.rb +0 -190
- data/spec/httparty/request_spec.rb +0 -1279
- data/spec/httparty/response_spec.rb +0 -347
- data/spec/httparty/ssl_spec.rb +0 -74
- data/spec/httparty_spec.rb +0 -878
- data/spec/spec_helper.rb +0 -51
- data/spec/support/ssl_test_helper.rb +0 -47
- data/spec/support/ssl_test_server.rb +0 -80
- data/spec/support/stub_response.rb +0 -49
    
        data/examples/stream_download.rb
    CHANGED
    
    | @@ -9,8 +9,14 @@ url = "https://cdn.kernel.org/pub/linux/kernel/v4.x/#{filename}" | |
| 9 9 |  | 
| 10 10 | 
             
            File.open(filename, "w") do |file|
         | 
| 11 11 | 
             
              response = HTTParty.get(url, stream_body: true) do |fragment|
         | 
| 12 | 
            -
                 | 
| 13 | 
            -
             | 
| 12 | 
            +
                if [301, 302].include?(fragment.code)
         | 
| 13 | 
            +
                  print "skip writing for redirect"
         | 
| 14 | 
            +
                elsif fragment.code == 200
         | 
| 15 | 
            +
                  print "."
         | 
| 16 | 
            +
                  file.write(fragment)
         | 
| 17 | 
            +
                else
         | 
| 18 | 
            +
                  raise StandardError, "Non-success status code while streaming #{fragment.code}"
         | 
| 19 | 
            +
                end
         | 
| 14 20 | 
             
              end
         | 
| 15 21 | 
             
            end
         | 
| 16 22 | 
             
            puts
         | 
    
        data/httparty.gemspec
    CHANGED
    
    | @@ -9,13 +9,14 @@ Gem::Specification.new do |s| | |
| 9 9 | 
             
              s.licenses    = ['MIT']
         | 
| 10 10 | 
             
              s.authors     = ["John Nunemaker", "Sandro Turriate"]
         | 
| 11 11 | 
             
              s.email       = ["nunemaker@gmail.com"]
         | 
| 12 | 
            -
              s.homepage    = " | 
| 12 | 
            +
              s.homepage    = "https://github.com/jnunemaker/httparty"
         | 
| 13 13 | 
             
              s.summary     = 'Makes http fun! Also, makes consuming restful web services dead easy.'
         | 
| 14 14 | 
             
              s.description = 'Makes http fun! Also, makes consuming restful web services dead easy.'
         | 
| 15 15 |  | 
| 16 | 
            -
              s.required_ruby_version     = '>= 2. | 
| 16 | 
            +
              s.required_ruby_version     = '>= 2.3.0'
         | 
| 17 17 |  | 
| 18 18 | 
             
              s.add_dependency 'multi_xml', ">= 0.5.2"
         | 
| 19 | 
            +
              s.add_dependency 'mini_mime', ">= 1.0.0"
         | 
| 19 20 |  | 
| 20 21 | 
             
              # If this line is removed, all hard partying will cease.
         | 
| 21 22 | 
             
              s.post_install_message = "When you HTTParty, you must party hard!"
         | 
| @@ -24,7 +25,6 @@ Gem::Specification.new do |s| | |
| 24 25 | 
             
              test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
         | 
| 25 26 |  | 
| 26 27 | 
             
              s.files         = all_files - test_files
         | 
| 27 | 
            -
              s.test_files    = test_files
         | 
| 28 28 | 
             
              s.executables   = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
         | 
| 29 29 | 
             
              s.require_paths = ["lib"]
         | 
| 30 30 | 
             
            end
         | 
| @@ -1,10 +1,12 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module HTTParty
         | 
| 2 4 | 
             
              # Default connection adapter that returns a new Net::HTTP each time
         | 
| 3 5 | 
             
              #
         | 
| 4 6 | 
             
              # == Custom Connection Factories
         | 
| 5 7 | 
             
              #
         | 
| 6 8 | 
             
              # If you like to implement your own connection adapter, subclassing
         | 
| 7 | 
            -
              #  | 
| 9 | 
            +
              # HTTParty::ConnectionAdapter will make it easier. Just override
         | 
| 8 10 | 
             
              # the #connection method. The uri and options attributes will have
         | 
| 9 11 | 
             
              # all the info you need to construct your http connection. Whatever
         | 
| 10 12 | 
             
              # you return from your connection method needs to adhere to the
         | 
| @@ -38,12 +40,13 @@ module HTTParty | |
| 38 40 | 
             
              # in the #options attribute. It is up to you to interpret them within your
         | 
| 39 41 | 
             
              # connection adapter. Take a look at the implementation of
         | 
| 40 42 | 
             
              # HTTParty::ConnectionAdapter#connection for examples of how they are used.
         | 
| 41 | 
            -
              # The keys used in options are | 
| 43 | 
            +
              # The keys used in options are
         | 
| 42 44 | 
             
              # * :+timeout+: timeout in seconds
         | 
| 43 45 | 
             
              # * :+open_timeout+: http connection open_timeout in seconds, overrides timeout if set
         | 
| 44 46 | 
             
              # * :+read_timeout+: http connection read_timeout in seconds, overrides timeout if set
         | 
| 47 | 
            +
              # * :+write_timeout+: http connection write_timeout in seconds, overrides timeout if set (Ruby >= 2.6.0 required)
         | 
| 45 48 | 
             
              # * :+debug_output+: see HTTParty::ClassMethods.debug_output.
         | 
| 46 | 
            -
              # * :+cert_store+: contains certificate data. see method 'attach_ssl_certificates' | 
| 49 | 
            +
              # * :+cert_store+: contains certificate data. see method 'attach_ssl_certificates'
         | 
| 47 50 | 
             
              # * :+pem+: contains pem client certificate data. see method 'attach_ssl_certificates'
         | 
| 48 51 | 
             
              # * :+p12+: contains PKCS12 client client certificate data.  see method 'attach_ssl_certificates'
         | 
| 49 52 | 
             
              # * :+verify+: verify the server’s certificate against the ca certificate.
         | 
| @@ -77,6 +80,12 @@ module HTTParty | |
| 77 80 | 
             
                  new(uri, options).connection
         | 
| 78 81 | 
             
                end
         | 
| 79 82 |  | 
| 83 | 
            +
                def self.default_cert_store
         | 
| 84 | 
            +
                  @default_cert_store ||= OpenSSL::X509::Store.new.tap do |cert_store|
         | 
| 85 | 
            +
                    cert_store.set_default_paths
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 80 89 | 
             
                attr_reader :uri, :options
         | 
| 81 90 |  | 
| 82 91 | 
             
                def initialize(uri, options = {})
         | 
| @@ -91,7 +100,14 @@ module HTTParty | |
| 91 100 | 
             
                  host = clean_host(uri.host)
         | 
| 92 101 | 
             
                  port = uri.port || (uri.scheme == 'https' ? 443 : 80)
         | 
| 93 102 | 
             
                  if options.key?(:http_proxyaddr)
         | 
| 94 | 
            -
                    http = Net::HTTP.new( | 
| 103 | 
            +
                    http = Net::HTTP.new(
         | 
| 104 | 
            +
                      host,
         | 
| 105 | 
            +
                      port,
         | 
| 106 | 
            +
                      options[:http_proxyaddr],
         | 
| 107 | 
            +
                      options[:http_proxyport],
         | 
| 108 | 
            +
                      options[:http_proxyuser],
         | 
| 109 | 
            +
                      options[:http_proxypass]
         | 
| 110 | 
            +
                    )
         | 
| 95 111 | 
             
                  else
         | 
| 96 112 | 
             
                    http = Net::HTTP.new(host, port)
         | 
| 97 113 | 
             
                  end
         | 
| @@ -100,19 +116,35 @@ module HTTParty | |
| 100 116 |  | 
| 101 117 | 
             
                  attach_ssl_certificates(http, options)
         | 
| 102 118 |  | 
| 103 | 
            -
                  if  | 
| 119 | 
            +
                  if add_timeout?(options[:timeout])
         | 
| 104 120 | 
             
                    http.open_timeout = options[:timeout]
         | 
| 105 121 | 
             
                    http.read_timeout = options[:timeout]
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    from_ruby_version('2.6.0', option: :write_timeout, warn: false) do
         | 
| 124 | 
            +
                      http.write_timeout = options[:timeout]
         | 
| 125 | 
            +
                    end
         | 
| 106 126 | 
             
                  end
         | 
| 107 127 |  | 
| 108 | 
            -
                  if  | 
| 128 | 
            +
                  if add_timeout?(options[:read_timeout])
         | 
| 109 129 | 
             
                    http.read_timeout = options[:read_timeout]
         | 
| 110 130 | 
             
                  end
         | 
| 111 131 |  | 
| 112 | 
            -
                  if  | 
| 132 | 
            +
                  if add_timeout?(options[:open_timeout])
         | 
| 113 133 | 
             
                    http.open_timeout = options[:open_timeout]
         | 
| 114 134 | 
             
                  end
         | 
| 115 135 |  | 
| 136 | 
            +
                  if add_timeout?(options[:write_timeout])
         | 
| 137 | 
            +
                    from_ruby_version('2.6.0', option: :write_timeout) do
         | 
| 138 | 
            +
                      http.write_timeout = options[:write_timeout]
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  if add_max_retries?(options[:max_retries])
         | 
| 143 | 
            +
                    from_ruby_version('2.5.0', option: :max_retries) do
         | 
| 144 | 
            +
                      http.max_retries = options[:max_retries]
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
             | 
| 116 148 | 
             
                  if options[:debug_output]
         | 
| 117 149 | 
             
                    http.set_debug_output(options[:debug_output])
         | 
| 118 150 | 
             
                  end
         | 
| @@ -125,18 +157,14 @@ module HTTParty | |
| 125 157 | 
             
                  #
         | 
| 126 158 | 
             
                  # @see https://bugs.ruby-lang.org/issues/6617
         | 
| 127 159 | 
             
                  if options[:local_host]
         | 
| 128 | 
            -
                     | 
| 160 | 
            +
                    from_ruby_version('2.0.0', option: :local_host) do
         | 
| 129 161 | 
             
                      http.local_host = options[:local_host]
         | 
| 130 | 
            -
                    else
         | 
| 131 | 
            -
                      Kernel.warn("Warning: option :local_host requires Ruby version 2.0 or later")
         | 
| 132 162 | 
             
                    end
         | 
| 133 163 | 
             
                  end
         | 
| 134 164 |  | 
| 135 165 | 
             
                  if options[:local_port]
         | 
| 136 | 
            -
                     | 
| 166 | 
            +
                    from_ruby_version('2.0.0', option: :local_port) do
         | 
| 137 167 | 
             
                      http.local_port = options[:local_port]
         | 
| 138 | 
            -
                    else
         | 
| 139 | 
            -
                      Kernel.warn("Warning: option :local_port requires Ruby version 2.0 or later")
         | 
| 140 168 | 
             
                    end
         | 
| 141 169 | 
             
                  end
         | 
| 142 170 |  | 
| @@ -145,6 +173,22 @@ module HTTParty | |
| 145 173 |  | 
| 146 174 | 
             
                private
         | 
| 147 175 |  | 
| 176 | 
            +
                def from_ruby_version(ruby_version, option: nil, warn: true)
         | 
| 177 | 
            +
                  if RUBY_VERSION >= ruby_version
         | 
| 178 | 
            +
                    yield
         | 
| 179 | 
            +
                  elsif warn
         | 
| 180 | 
            +
                    Kernel.warn("Warning: option #{ option } requires Ruby version #{ ruby_version } or later")
         | 
| 181 | 
            +
                  end
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                def add_timeout?(timeout)
         | 
| 185 | 
            +
                  timeout && (timeout.is_a?(Integer) || timeout.is_a?(Float))
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                def add_max_retries?(max_retries)
         | 
| 189 | 
            +
                  max_retries && max_retries.is_a?(Integer) && max_retries >= 0
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 148 192 | 
             
                def clean_host(host)
         | 
| 149 193 | 
             
                  strip_ipv6_brackets(host)
         | 
| 150 194 | 
             
                end
         | 
| @@ -169,8 +213,7 @@ module HTTParty | |
| 169 213 | 
             
                        http.cert_store = options[:cert_store]
         | 
| 170 214 | 
             
                      else
         | 
| 171 215 | 
             
                        # Use the default cert store by default, i.e. system ca certs
         | 
| 172 | 
            -
                        http.cert_store =  | 
| 173 | 
            -
                        http.cert_store.set_default_paths
         | 
| 216 | 
            +
                        http.cert_store = self.class.default_cert_store
         | 
| 174 217 | 
             
                      end
         | 
| 175 218 | 
             
                    else
         | 
| 176 219 | 
             
                      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
         | 
| @@ -180,7 +223,7 @@ module HTTParty | |
| 180 223 | 
             
                    # Note: options[:pem] must contain the content of a PEM file having the private key appended
         | 
| 181 224 | 
             
                    if options[:pem]
         | 
| 182 225 | 
             
                      http.cert = OpenSSL::X509::Certificate.new(options[:pem])
         | 
| 183 | 
            -
                      http.key = OpenSSL::PKey | 
| 226 | 
            +
                      http.key = OpenSSL::PKey.read(options[:pem], options[:pem_password])
         | 
| 184 227 | 
             
                      http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
         | 
| 185 228 | 
             
                    end
         | 
| 186 229 |  | 
    
        data/lib/httparty/cookie_hash.rb
    CHANGED
    
    | @@ -1,14 +1,16 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            class HTTParty::CookieHash < Hash #:nodoc:
         | 
| 2 | 
            -
              CLIENT_COOKIES = %w(path expires domain path secure httponly)
         | 
| 4 | 
            +
              CLIENT_COOKIES = %w(path expires domain path secure httponly samesite)
         | 
| 3 5 |  | 
| 4 | 
            -
              def add_cookies( | 
| 5 | 
            -
                case  | 
| 6 | 
            +
              def add_cookies(data)
         | 
| 7 | 
            +
                case data
         | 
| 6 8 | 
             
                when Hash
         | 
| 7 | 
            -
                  merge!( | 
| 9 | 
            +
                  merge!(data)
         | 
| 8 10 | 
             
                when String
         | 
| 9 | 
            -
                   | 
| 10 | 
            -
                     | 
| 11 | 
            -
                    self[ | 
| 11 | 
            +
                  data.split('; ').each do |cookie|
         | 
| 12 | 
            +
                    key, value = cookie.split('=', 2)
         | 
| 13 | 
            +
                    self[key.to_sym] = value if key
         | 
| 12 14 | 
             
                  end
         | 
| 13 15 | 
             
                else
         | 
| 14 16 | 
             
                  raise "add_cookies only takes a Hash or a String"
         | 
| @@ -16,6 +18,6 @@ class HTTParty::CookieHash < Hash #:nodoc: | |
| 16 18 | 
             
              end
         | 
| 17 19 |  | 
| 18 20 | 
             
              def to_cookie_string
         | 
| 19 | 
            -
                 | 
| 21 | 
            +
                select { |k, v| !CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join('; ')
         | 
| 20 22 | 
             
              end
         | 
| 21 23 | 
             
            end
         | 
| @@ -0,0 +1,102 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module HTTParty
         | 
| 4 | 
            +
              # Decompresses the response body based on the Content-Encoding header.
         | 
| 5 | 
            +
              #
         | 
| 6 | 
            +
              # Net::HTTP automatically decompresses Content-Encoding values "gzip" and "deflate".
         | 
| 7 | 
            +
              # This class will handle "br" (Brotli) and "compress" (LZW) if the requisite
         | 
| 8 | 
            +
              # gems are installed. Otherwise, it returns nil if the body data cannot be
         | 
| 9 | 
            +
              # decompressed.
         | 
| 10 | 
            +
              #
         | 
| 11 | 
            +
              # @abstract Read the HTTP Compression section for more information.
         | 
| 12 | 
            +
              class Decompressor
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # "gzip" and "deflate" are handled by Net::HTTP
         | 
| 15 | 
            +
                # hence they do not need to be handled by HTTParty
         | 
| 16 | 
            +
                SupportedEncodings = {
         | 
| 17 | 
            +
                  'none'     => :none,
         | 
| 18 | 
            +
                  'identity' => :none,
         | 
| 19 | 
            +
                  'br'       => :brotli,
         | 
| 20 | 
            +
                  'compress' => :lzw,
         | 
| 21 | 
            +
                  'zstd'     => :zstd
         | 
| 22 | 
            +
                }.freeze
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                # The response body of the request
         | 
| 25 | 
            +
                # @return [String]
         | 
| 26 | 
            +
                attr_reader :body
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                # The Content-Encoding algorithm used to encode the body
         | 
| 29 | 
            +
                # @return [Symbol] e.g. :gzip
         | 
| 30 | 
            +
                attr_reader :encoding
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # @param [String] body - the response body of the request
         | 
| 33 | 
            +
                # @param [Symbol] encoding - the Content-Encoding algorithm used to encode the body
         | 
| 34 | 
            +
                def initialize(body, encoding)
         | 
| 35 | 
            +
                  @body = body
         | 
| 36 | 
            +
                  @encoding = encoding
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                # Perform decompression on the response body
         | 
| 40 | 
            +
                # @return [String] the decompressed body
         | 
| 41 | 
            +
                # @return [nil] when the response body is nil or cannot decompressed
         | 
| 42 | 
            +
                def decompress
         | 
| 43 | 
            +
                  return nil if body.nil?
         | 
| 44 | 
            +
                  return body if encoding.nil? || encoding.strip.empty?
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  if supports_encoding?
         | 
| 47 | 
            +
                    decompress_supported_encoding
         | 
| 48 | 
            +
                  else
         | 
| 49 | 
            +
                    nil
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                protected
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def supports_encoding?
         | 
| 56 | 
            +
                  SupportedEncodings.keys.include?(encoding)
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def decompress_supported_encoding
         | 
| 60 | 
            +
                  method = SupportedEncodings[encoding]
         | 
| 61 | 
            +
                  if respond_to?(method, true)
         | 
| 62 | 
            +
                    send(method)
         | 
| 63 | 
            +
                  else
         | 
| 64 | 
            +
                    raise NotImplementedError, "#{self.class.name} has not implemented a decompression method for #{encoding.inspect} encoding."
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def none
         | 
| 69 | 
            +
                  body
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                def brotli
         | 
| 73 | 
            +
                  return nil unless defined?(::Brotli)
         | 
| 74 | 
            +
                  begin
         | 
| 75 | 
            +
                    ::Brotli.inflate(body)
         | 
| 76 | 
            +
                  rescue StandardError
         | 
| 77 | 
            +
                    nil
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def lzw
         | 
| 82 | 
            +
                  begin
         | 
| 83 | 
            +
                    if defined?(::LZWS::String)
         | 
| 84 | 
            +
                      ::LZWS::String.decompress(body)
         | 
| 85 | 
            +
                    elsif defined?(::LZW::Simple)
         | 
| 86 | 
            +
                      ::LZW::Simple.new.decompress(body)
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
                  rescue StandardError
         | 
| 89 | 
            +
                    nil
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def zstd
         | 
| 94 | 
            +
                  return nil unless defined?(::Zstd)
         | 
| 95 | 
            +
                  begin
         | 
| 96 | 
            +
                    ::Zstd.decompress(body)
         | 
| 97 | 
            +
                  rescue StandardError
         | 
| 98 | 
            +
                    nil
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
                end
         | 
| 101 | 
            +
              end
         | 
| 102 | 
            +
            end
         | 
    
        data/lib/httparty/exceptions.rb
    CHANGED
    
    | @@ -1,5 +1,7 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module HTTParty
         | 
| 2 | 
            -
              # @ | 
| 4 | 
            +
              # @abstract Exceptions raised by HTTParty inherit from Error
         | 
| 3 5 | 
             
              class Error < StandardError; end
         | 
| 4 6 |  | 
| 5 7 | 
             
              # Exception raised when you attempt to set a non-existent format
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'erb'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module HTTParty
         | 
| @@ -24,32 +26,46 @@ module HTTParty | |
| 24 26 | 
             
                #
         | 
| 25 27 | 
             
                # @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
         | 
| 26 28 | 
             
                def self.normalize_param(key, value)
         | 
| 27 | 
            -
                   | 
| 29 | 
            +
                  normalized_keys = normalize_keys(key, value)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  normalized_keys.flatten.each_slice(2).inject(''.dup) do |string, (k, v)|
         | 
| 32 | 
            +
                    string << "#{ERB::Util.url_encode(k)}=#{ERB::Util.url_encode(v.to_s)}&"
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def self.normalize_keys(key, value)
         | 
| 28 37 | 
             
                  stack = []
         | 
| 38 | 
            +
                  normalized_keys = []
         | 
| 29 39 |  | 
| 30 40 | 
             
                  if value.respond_to?(:to_ary)
         | 
| 31 | 
            -
                     | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 41 | 
            +
                    if value.empty?
         | 
| 42 | 
            +
                      normalized_keys << ["#{key}[]", '']
         | 
| 43 | 
            +
                    else
         | 
| 44 | 
            +
                      normalized_keys = value.to_ary.flat_map do |element|
         | 
| 45 | 
            +
                        normalize_keys("#{key}[]", element)
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
                    end
         | 
| 36 48 | 
             
                  elsif value.respond_to?(:to_hash)
         | 
| 37 49 | 
             
                    stack << [key, value.to_hash]
         | 
| 38 50 | 
             
                  else
         | 
| 39 | 
            -
                     | 
| 51 | 
            +
                    normalized_keys << [key.to_s, value]
         | 
| 40 52 | 
             
                  end
         | 
| 41 53 |  | 
| 42 54 | 
             
                  stack.each do |parent, hash|
         | 
| 43 | 
            -
                    hash.each do | | 
| 44 | 
            -
                      if  | 
| 45 | 
            -
                        stack << ["#{parent}[#{ | 
| 55 | 
            +
                    hash.each do |child_key, child_value|
         | 
| 56 | 
            +
                      if child_value.respond_to?(:to_hash)
         | 
| 57 | 
            +
                        stack << ["#{parent}[#{child_key}]", child_value.to_hash]
         | 
| 58 | 
            +
                      elsif child_value.respond_to?(:to_ary)
         | 
| 59 | 
            +
                        child_value.to_ary.each do |v|
         | 
| 60 | 
            +
                          normalized_keys << normalize_keys("#{parent}[#{child_key}][]", v).flatten
         | 
| 61 | 
            +
                        end
         | 
| 46 62 | 
             
                      else
         | 
| 47 | 
            -
                         | 
| 63 | 
            +
                        normalized_keys << normalize_keys("#{parent}[#{child_key}]", child_value).flatten
         | 
| 48 64 | 
             
                      end
         | 
| 49 65 | 
             
                    end
         | 
| 50 66 | 
             
                  end
         | 
| 51 67 |  | 
| 52 | 
            -
                   | 
| 68 | 
            +
                  normalized_keys
         | 
| 53 69 | 
             
                end
         | 
| 54 70 | 
             
              end
         | 
| 55 71 | 
             
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module HTTParty
         | 
| 4 | 
            +
              class HeadersProcessor
         | 
| 5 | 
            +
                attr_reader :headers, :options
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(headers, options)
         | 
| 8 | 
            +
                  @headers = headers
         | 
| 9 | 
            +
                  @options = options
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def call
         | 
| 13 | 
            +
                  return unless options[:headers]
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  options[:headers] = headers.merge(options[:headers]) if headers.any?
         | 
| 16 | 
            +
                  options[:headers] = Utils.stringify_keys(process_dynamic_headers)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                private
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def process_dynamic_headers
         | 
| 22 | 
            +
                  options[:headers].each_with_object({}) do |header, processed_headers|
         | 
| 23 | 
            +
                    key, value = header
         | 
| 24 | 
            +
                    processed_headers[key] = if value.respond_to?(:call)
         | 
| 25 | 
            +
                                               value.arity == 0 ? value.call : value.call(options)
         | 
| 26 | 
            +
                                             else
         | 
| 27 | 
            +
                                               value
         | 
| 28 | 
            +
                                             end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -1,9 +1,11 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module HTTParty
         | 
| 2 4 | 
             
              module Logger
         | 
| 3 5 | 
             
                class ApacheFormatter #:nodoc:
         | 
| 4 6 | 
             
                  TAG_NAME = HTTParty.name
         | 
| 5 7 |  | 
| 6 | 
            -
                  attr_accessor :level, :logger | 
| 8 | 
            +
                  attr_accessor :level, :logger
         | 
| 7 9 |  | 
| 8 10 | 
             
                  def initialize(logger, level)
         | 
| 9 11 | 
             
                    @logger = logger
         | 
| @@ -11,11 +13,34 @@ module HTTParty | |
| 11 13 | 
             
                  end
         | 
| 12 14 |  | 
| 13 15 | 
             
                  def format(request, response)
         | 
| 14 | 
            -
                     | 
| 15 | 
            -
                     | 
| 16 | 
            -
             | 
| 17 | 
            -
                     | 
| 18 | 
            -
             | 
| 16 | 
            +
                    @request = request
         | 
| 17 | 
            +
                    @response = response
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    logger.public_send level, message
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  private
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  attr_reader :request, :response
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def message
         | 
| 27 | 
            +
                    "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def current_time
         | 
| 31 | 
            +
                    Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  def http_method
         | 
| 35 | 
            +
                    request.http_method.name.split('::').last.upcase
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def path
         | 
| 39 | 
            +
                    request.path.to_s
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def content_length
         | 
| 43 | 
            +
                    response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
         | 
| 19 44 | 
             
                  end
         | 
| 20 45 | 
             
                end
         | 
| 21 46 | 
             
              end
         | 
| @@ -1,9 +1,11 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module HTTParty
         | 
| 2 4 | 
             
              module Logger
         | 
| 3 5 | 
             
                class CurlFormatter #:nodoc:
         | 
| 4 6 | 
             
                  TAG_NAME = HTTParty.name
         | 
| 5 | 
            -
                  OUT      = '>' | 
| 6 | 
            -
                  IN       = '<' | 
| 7 | 
            +
                  OUT      = '>'
         | 
| 8 | 
            +
                  IN       = '<'
         | 
| 7 9 |  | 
| 8 10 | 
             
                  attr_accessor :level, :logger
         | 
| 9 11 |  | 
| @@ -20,7 +22,7 @@ module HTTParty | |
| 20 22 | 
             
                    log_request
         | 
| 21 23 | 
             
                    log_response
         | 
| 22 24 |  | 
| 23 | 
            -
                    logger. | 
| 25 | 
            +
                    logger.public_send level, messages.join("\n")
         | 
| 24 26 | 
             
                  end
         | 
| 25 27 |  | 
| 26 28 | 
             
                  private
         | 
| @@ -44,7 +46,7 @@ module HTTParty | |
| 44 46 | 
             
                  end
         | 
| 45 47 |  | 
| 46 48 | 
             
                  def log_url
         | 
| 47 | 
            -
                    http_method = request.http_method.name.split( | 
| 49 | 
            +
                    http_method = request.http_method.name.split('::').last.upcase
         | 
| 48 50 | 
             
                    uri = if request.options[:base_uri]
         | 
| 49 51 | 
             
                            request.options[:base_uri] + request.path.path
         | 
| 50 52 | 
             
                          else
         | 
| @@ -80,11 +82,11 @@ module HTTParty | |
| 80 82 | 
             
                  end
         | 
| 81 83 |  | 
| 82 84 | 
             
                  def log(direction, line = '')
         | 
| 83 | 
            -
                    messages << "[#{TAG_NAME}] [#{ | 
| 85 | 
            +
                    messages << "[#{TAG_NAME}] [#{current_time}] #{direction} #{line}"
         | 
| 84 86 | 
             
                  end
         | 
| 85 87 |  | 
| 86 | 
            -
                  def  | 
| 87 | 
            -
                     | 
| 88 | 
            +
                  def current_time
         | 
| 89 | 
            +
                    Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
         | 
| 88 90 | 
             
                  end
         | 
| 89 91 | 
             
                end
         | 
| 90 92 | 
             
              end
         | 
| @@ -1,12 +1,16 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'httparty/logger/apache_formatter'
         | 
| 2 4 | 
             
            require 'httparty/logger/curl_formatter'
         | 
| 5 | 
            +
            require 'httparty/logger/logstash_formatter'
         | 
| 3 6 |  | 
| 4 7 | 
             
            module HTTParty
         | 
| 5 8 | 
             
              module Logger
         | 
| 6 9 | 
             
                def self.formatters
         | 
| 7 10 | 
             
                  @formatters ||= {
         | 
| 8 11 | 
             
                    :curl => Logger::CurlFormatter,
         | 
| 9 | 
            -
                    :apache => Logger::ApacheFormatter
         | 
| 12 | 
            +
                    :apache => Logger::ApacheFormatter,
         | 
| 13 | 
            +
                    :logstash => Logger::LogstashFormatter,
         | 
| 10 14 | 
             
                  }
         | 
| 11 15 | 
             
                end
         | 
| 12 16 |  | 
| @@ -0,0 +1,61 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module HTTParty
         | 
| 4 | 
            +
              module Logger
         | 
| 5 | 
            +
                class LogstashFormatter #:nodoc:
         | 
| 6 | 
            +
                  TAG_NAME = HTTParty.name
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  attr_accessor :level, :logger
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  def initialize(logger, level)
         | 
| 11 | 
            +
                    @logger = logger
         | 
| 12 | 
            +
                    @level  = level.to_sym
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def format(request, response)
         | 
| 16 | 
            +
                    @request = request
         | 
| 17 | 
            +
                    @response = response
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    logger.public_send level, logstash_message
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  private
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  attr_reader :request, :response
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def logstash_message
         | 
| 27 | 
            +
                    {
         | 
| 28 | 
            +
                      '@timestamp' => current_time,
         | 
| 29 | 
            +
                      '@version' => 1,
         | 
| 30 | 
            +
                      'content_length' => content_length || '-',
         | 
| 31 | 
            +
                      'http_method' => http_method,
         | 
| 32 | 
            +
                      'message' => message,
         | 
| 33 | 
            +
                      'path' => path,
         | 
| 34 | 
            +
                      'response_code' => response.code,
         | 
| 35 | 
            +
                      'severity' => level,
         | 
| 36 | 
            +
                      'tags' => [TAG_NAME],
         | 
| 37 | 
            +
                    }.to_json
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def message
         | 
| 41 | 
            +
                    "[#{TAG_NAME}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def current_time
         | 
| 45 | 
            +
                    Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def http_method
         | 
| 49 | 
            +
                    @http_method ||= request.http_method.name.split('::').last.upcase
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  def path
         | 
| 53 | 
            +
                    @path ||= request.path.to_s
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def content_length
         | 
| 57 | 
            +
                    @content_length ||= response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
            end
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module HTTParty
         | 
| 2 4 | 
             
              module ModuleInheritableAttributes #:nodoc:
         | 
| 3 5 | 
             
                def self.included(base)
         | 
| @@ -9,12 +11,12 @@ module HTTParty | |
| 9 11 | 
             
                  duplicate = hash.dup
         | 
| 10 12 |  | 
| 11 13 | 
             
                  duplicate.each_pair do |key, value|
         | 
| 12 | 
            -
                     | 
| 13 | 
            -
             | 
| 14 | 
            +
                    if value.is_a?(Hash)
         | 
| 15 | 
            +
                      duplicate[key] = hash_deep_dup(value)
         | 
| 14 16 | 
             
                    elsif value.is_a?(Proc)
         | 
| 15 17 | 
             
                      duplicate[key] = value.dup
         | 
| 16 18 | 
             
                    else
         | 
| 17 | 
            -
                      value
         | 
| 19 | 
            +
                      duplicate[key] = value
         | 
| 18 20 | 
             
                    end
         | 
| 19 21 | 
             
                  end
         | 
| 20 22 |  | 
| @@ -36,7 +38,7 @@ module HTTParty | |
| 36 38 | 
             
                  def inherited(subclass)
         | 
| 37 39 | 
             
                    super
         | 
| 38 40 | 
             
                    @mattr_inheritable_attrs.each do |inheritable_attribute|
         | 
| 39 | 
            -
                      ivar = "@#{inheritable_attribute}"
         | 
| 41 | 
            +
                      ivar = :"@#{inheritable_attribute}"
         | 
| 40 42 | 
             
                      subclass.instance_variable_set(ivar, instance_variable_get(ivar).clone)
         | 
| 41 43 |  | 
| 42 44 | 
             
                      if instance_variable_get(ivar).respond_to?(:merge)
         |