httparty 0.16.2 → 0.22.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.
Files changed (96) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +18 -0
  3. data/.github/dependabot.yml +6 -0
  4. data/.github/workflows/ci.yml +23 -0
  5. data/.gitignore +2 -0
  6. data/.rubocop_todo.yml +1 -1
  7. data/Changelog.md +425 -280
  8. data/Gemfile +7 -0
  9. data/Guardfile +3 -2
  10. data/README.md +5 -5
  11. data/docs/README.md +90 -5
  12. data/examples/README.md +28 -11
  13. data/examples/aaws.rb +6 -2
  14. data/examples/body_stream.rb +14 -0
  15. data/examples/idn.rb +10 -0
  16. data/examples/microsoft_graph.rb +52 -0
  17. data/examples/multipart.rb +22 -0
  18. data/examples/peer_cert.rb +9 -0
  19. data/examples/stream_download.rb +8 -2
  20. data/httparty.gemspec +4 -3
  21. data/lib/httparty/connection_adapter.rb +44 -20
  22. data/lib/httparty/cookie_hash.rb +10 -8
  23. data/lib/httparty/decompressor.rb +102 -0
  24. data/lib/httparty/exceptions.rb +3 -1
  25. data/lib/httparty/hash_conversions.rb +10 -4
  26. data/lib/httparty/headers_processor.rb +32 -0
  27. data/lib/httparty/logger/apache_formatter.rb +31 -6
  28. data/lib/httparty/logger/curl_formatter.rb +9 -7
  29. data/lib/httparty/logger/logger.rb +5 -1
  30. data/lib/httparty/logger/logstash_formatter.rb +62 -0
  31. data/lib/httparty/module_inheritable_attributes.rb +9 -9
  32. data/lib/httparty/net_digest_auth.rb +15 -15
  33. data/lib/httparty/parser.rb +12 -5
  34. data/lib/httparty/request/body.rb +54 -27
  35. data/lib/httparty/request/multipart_boundary.rb +2 -0
  36. data/lib/httparty/request.rb +105 -107
  37. data/lib/httparty/response/headers.rb +4 -2
  38. data/lib/httparty/response.rb +52 -9
  39. data/lib/httparty/response_fragment.rb +21 -0
  40. data/lib/httparty/text_encoder.rb +72 -0
  41. data/lib/httparty/utils.rb +13 -0
  42. data/lib/httparty/version.rb +3 -1
  43. data/lib/httparty.rb +81 -33
  44. data/script/release +4 -4
  45. data/website/css/common.css +1 -1
  46. metadata +50 -107
  47. data/.simplecov +0 -1
  48. data/.travis.yml +0 -10
  49. data/features/basic_authentication.feature +0 -20
  50. data/features/command_line.feature +0 -95
  51. data/features/deals_with_http_error_codes.feature +0 -26
  52. data/features/digest_authentication.feature +0 -30
  53. data/features/handles_compressed_responses.feature +0 -27
  54. data/features/handles_multiple_formats.feature +0 -57
  55. data/features/steps/env.rb +0 -27
  56. data/features/steps/httparty_response_steps.rb +0 -56
  57. data/features/steps/httparty_steps.rb +0 -43
  58. data/features/steps/mongrel_helper.rb +0 -127
  59. data/features/steps/remote_service_steps.rb +0 -92
  60. data/features/supports_read_timeout_option.feature +0 -13
  61. data/features/supports_redirection.feature +0 -22
  62. data/features/supports_timeout_option.feature +0 -13
  63. data/spec/fixtures/delicious.xml +0 -23
  64. data/spec/fixtures/empty.xml +0 -0
  65. data/spec/fixtures/google.html +0 -3
  66. data/spec/fixtures/ssl/generate.sh +0 -29
  67. data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
  68. data/spec/fixtures/ssl/generated/ca.crt +0 -16
  69. data/spec/fixtures/ssl/generated/ca.key +0 -15
  70. data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
  71. data/spec/fixtures/ssl/generated/server.crt +0 -13
  72. data/spec/fixtures/ssl/generated/server.key +0 -15
  73. data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
  74. data/spec/fixtures/tiny.gif +0 -0
  75. data/spec/fixtures/twitter.csv +0 -2
  76. data/spec/fixtures/twitter.json +0 -1
  77. data/spec/fixtures/twitter.xml +0 -403
  78. data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
  79. data/spec/httparty/connection_adapter_spec.rb +0 -498
  80. data/spec/httparty/cookie_hash_spec.rb +0 -100
  81. data/spec/httparty/exception_spec.rb +0 -45
  82. data/spec/httparty/hash_conversions_spec.rb +0 -56
  83. data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
  84. data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
  85. data/spec/httparty/logger/logger_spec.rb +0 -38
  86. data/spec/httparty/net_digest_auth_spec.rb +0 -270
  87. data/spec/httparty/parser_spec.rb +0 -190
  88. data/spec/httparty/request/body_spec.rb +0 -60
  89. data/spec/httparty/request_spec.rb +0 -1312
  90. data/spec/httparty/response_spec.rb +0 -347
  91. data/spec/httparty/ssl_spec.rb +0 -74
  92. data/spec/httparty_spec.rb +0 -896
  93. data/spec/spec_helper.rb +0 -51
  94. data/spec/support/ssl_test_helper.rb +0 -47
  95. data/spec/support/ssl_test_server.rb +0 -80
  96. data/spec/support/stub_response.rb +0 -49
data/Gemfile CHANGED
@@ -3,6 +3,7 @@ gemspec
3
3
 
4
4
  gem 'rake'
5
5
  gem 'mongrel', '1.2.0.pre2'
6
+ gem 'json'
6
7
 
7
8
  group :development do
8
9
  gem 'guard'
@@ -11,9 +12,15 @@ group :development do
11
12
  end
12
13
 
13
14
  group :test do
15
+ gem 'rexml'
14
16
  gem 'rspec', '~> 3.4'
15
17
  gem 'simplecov', require: false
16
18
  gem 'aruba'
17
19
  gem 'cucumber', '~> 2.3'
18
20
  gem 'webmock'
21
+ gem 'addressable'
22
+ end
23
+
24
+ group :development, :test do
25
+ gem 'pry'
19
26
  end
data/Guardfile CHANGED
@@ -1,7 +1,8 @@
1
1
  rspec_options = {
2
- version: 1,
3
2
  all_after_pass: false,
4
- all_on_start: false
3
+ all_on_start: false,
4
+ failed_mode: :keep,
5
+ cmd: 'bundle exec rspec',
5
6
  }
6
7
 
7
8
  guard 'rspec', rspec_options do
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # httparty
2
2
 
3
+ [![CI](https://github.com/jnunemaker/httparty/actions/workflows/ci.yml/badge.svg)](https://github.com/jnunemaker/httparty/actions/workflows/ci.yml)
4
+
3
5
  Makes http fun again! Ain't no party like a httparty, because a httparty don't stop.
4
6
 
5
7
  ## Install
@@ -10,7 +12,7 @@ gem install httparty
10
12
 
11
13
  ## Requirements
12
14
 
13
- * Ruby 2.0.0 or higher
15
+ * Ruby 2.3.0 or higher
14
16
  * multi_xml
15
17
  * You like to party!
16
18
 
@@ -46,7 +48,6 @@ puts stack_exchange.users
46
48
  ```
47
49
 
48
50
  See the [examples directory](http://github.com/jnunemaker/httparty/tree/master/examples) for even more goodies.
49
-
50
51
  ## Command Line Interface
51
52
 
52
53
  httparty also includes the executable `httparty` which can be
@@ -63,9 +64,8 @@ httparty "https://api.stackexchange.com/2.2/questions?site=stackoverflow"
63
64
  ## Help and Docs
64
65
 
65
66
  * [Docs](https://github.com/jnunemaker/httparty/tree/master/docs)
66
- * https://groups.google.com/forum/#!forum/httparty-gem
67
- * http://rdoc.info/projects/jnunemaker/httparty
68
- * http://stackoverflow.com/questions/tagged/httparty
67
+ * https://github.com/jnunemaker/httparty/discussions
68
+ * https://www.rubydoc.info/github/jnunemaker/httparty
69
69
 
70
70
  ## Contributing
71
71
 
data/docs/README.md CHANGED
@@ -9,11 +9,25 @@ Makes http fun again!
9
9
  ## Parsing JSON
10
10
  If the response Content Type is `application/json`, HTTParty will parse the response and return Ruby objects such as a hash or array. The default behavior for parsing JSON will return keys as strings. This can be supressed with the `format` option. To get hash keys as symbols:
11
11
 
12
- ```
12
+ ```ruby
13
13
  response = HTTParty.get('http://example.com', format: :plain)
14
14
  JSON.parse response, symbolize_names: true
15
15
  ```
16
16
 
17
+ ## Posting JSON
18
+ When using Content Type `application/json` with `POST`, `PUT` or `PATCH` requests, the body should be a string of valid JSON:
19
+
20
+ ```ruby
21
+ # With written JSON
22
+ HTTParty.post('http://example.com', body: "{\"foo\":\"bar\"}", headers: { 'Content-Type' => 'application/json' })
23
+
24
+ # Using JSON.generate
25
+ HTTParty.post('http://example.com', body: JSON.generate({ foo: 'bar' }), headers: { 'Content-Type' => 'application/json' })
26
+
27
+ # Using object.to_json
28
+ HTTParty.post('http://example.com', body: { foo: 'bar' }.to_json, headers: { 'Content-Type' => 'application/json' })
29
+ ```
30
+
17
31
  ## Working with SSL
18
32
 
19
33
  You can use this guide to work with SSL certificates.
@@ -70,7 +84,7 @@ class Client
70
84
  end
71
85
  ```
72
86
 
73
- You can also include this options with the call:
87
+ You can also include all of these options with the call:
74
88
 
75
89
  ```ruby
76
90
  class Client
@@ -79,14 +93,14 @@ class Client
79
93
  base_uri "https://example.com"
80
94
 
81
95
  def self.fetch
82
- get("/resources", pem: (File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), "123456")
96
+ get("/resources", pem: File.read("#{File.expand_path('.')}/path/to/certs/cert.pem"), pem_password: "123456")
83
97
  end
84
98
  end
85
99
  ```
86
100
 
87
101
  ### Avoid SSL verification
88
102
 
89
- In some cases you may want to skip SSL verification, because the entity that issue the certificate is not a valid one, but you still want to work with it. You can achieve this through:
103
+ In some cases you may want to skip SSL verification, because the entity that issued the certificate is not a valid one, but you still want to work with it. You can achieve this through:
90
104
 
91
105
  ```ruby
92
106
  # Skips SSL certificate verification
@@ -103,4 +117,75 @@ class Client
103
117
  # get("resources", verify_peer: false)
104
118
  end
105
119
  end
106
- ```
120
+ ```
121
+
122
+ ### HTTP Compression
123
+
124
+ The `Accept-Encoding` request header and `Content-Encoding` response header
125
+ are used to control compression (gzip, etc.) over the wire. Refer to
126
+ [RFC-2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) for details.
127
+ (For clarity: these headers are **not** used for character encoding i.e. `utf-8`
128
+ which is specified in the `Accept` and `Content-Type` headers.)
129
+
130
+ Unless you have specific requirements otherwise, we recommend to **not** set
131
+ set the `Accept-Encoding` header on HTTParty requests. In this case, `Net::HTTP`
132
+ will set a sensible default compression scheme and automatically decompress the response.
133
+
134
+ If you explicitly set `Accept-Encoding`, there be dragons:
135
+
136
+ * If the HTTP response `Content-Encoding` received on the wire is `gzip` or `deflate`,
137
+ `Net::HTTP` will automatically decompress it, and will omit `Content-Encoding`
138
+ from your `HTTParty::Response` headers.
139
+
140
+ * For the following encodings, HTTParty will automatically decompress them if you include
141
+ the required gem into your project. Similar to above, if decompression succeeds,
142
+ `Content-Encoding` will be omitted from your `HTTParty::Response` headers.
143
+ **Warning:** Support for these encodings is experimental and not fully battle-tested.
144
+
145
+ | Content-Encoding | Required Gem |
146
+ | --- | --- |
147
+ | `br` (Brotli) | [brotli](https://rubygems.org/gems/brotli) |
148
+ | `compress` (LZW) | [ruby-lzws](https://rubygems.org/gems/ruby-lzws) |
149
+ | `zstd` (Zstandard) | [zstd-ruby](https://rubygems.org/gems/zstd-ruby) |
150
+
151
+ * For other encodings, `HTTParty::Response#body` will return the raw uncompressed byte string,
152
+ and you'll need to inspect the `Content-Encoding` response header and decompress it yourself.
153
+ In this case, `HTTParty::Response#parsed_response` will be `nil`.
154
+
155
+ * Lastly, you may use the `skip_decompression` option to disable all automatic decompression
156
+ and always get `HTTParty::Response#body` in its raw form along with the `Content-Encoding` header.
157
+
158
+ ```ruby
159
+ # Accept-Encoding=gzip,deflate can be safely assumed to be auto-decompressed
160
+
161
+ res = HTTParty.get('https://example.com/test.json', headers: { 'Accept-Encoding' => 'gzip,deflate,identity' })
162
+ JSON.parse(res.body) # safe
163
+
164
+
165
+ # Accept-Encoding=br,compress requires third-party gems
166
+
167
+ require 'brotli'
168
+ require 'lzws'
169
+ require 'zstd-ruby'
170
+ res = HTTParty.get('https://example.com/test.json', headers: { 'Accept-Encoding' => 'br,compress,zstd' })
171
+ JSON.parse(res.body)
172
+
173
+
174
+ # Accept-Encoding=* may return unhandled Content-Encoding
175
+
176
+ res = HTTParty.get('https://example.com/test.json', headers: { 'Accept-Encoding' => '*' })
177
+ encoding = res.headers['Content-Encoding']
178
+ if encoding
179
+ JSON.parse(your_decompression_handling(res.body, encoding))
180
+ else
181
+ # Content-Encoding not present implies decompressed
182
+ JSON.parse(res.body)
183
+ end
184
+
185
+
186
+ # Gimme the raw data!
187
+
188
+ res = HTTParty.get('https://example.com/test.json', skip_decompression: true)
189
+ encoding = res.headers['Content-Encoding']
190
+ JSON.parse(your_decompression_handling(res.body, encoding))
191
+ ```
data/examples/README.md CHANGED
@@ -13,22 +13,22 @@
13
13
  * Creates a custom parser for XML using crack gem
14
14
  * Uses `get` request
15
15
 
16
- * [Create HTML Nokogiri parser](nokogiri_html_parser.rb)
16
+ * [Create HTML Nokogiri parser](nokogiri_html_parser.rb)
17
17
  * Adds Html as a format
18
18
  * passed the body of request to Nokogiri
19
-
19
+
20
20
  * [More Custom Parsers](custom_parsers.rb)
21
21
  * Create an additional parser for atom or make it the ONLY parser
22
-
22
+
23
23
  * [Basic Auth, Delicious](delicious.rb)
24
24
  * Basic Auth, shows how to merge those into options
25
25
  * Uses `get` requests
26
-
26
+
27
27
  * [Passing Headers, User Agent](headers_and_user_agents.rb)
28
28
  * Use the class method of Httparty
29
29
  * Pass the User-Agent in the headers
30
30
  * Uses `get` requests
31
-
31
+
32
32
  * [Basic Post Request](basic.rb)
33
33
  * Httparty included into poro class
34
34
  * Uses `post` requests
@@ -36,7 +36,7 @@
36
36
  * [Access Rubyurl Shortener](rubyurl.rb)
37
37
  * Httparty included into poro class
38
38
  * Uses `post` requests
39
-
39
+
40
40
  * [Add a custom log file](logging.rb)
41
41
  * create a log file and have httparty log requests
42
42
 
@@ -44,23 +44,23 @@
44
44
  * Httparty included into poro class
45
45
  * Creates methods for different endpoints
46
46
  * Uses `get` requests
47
-
47
+
48
48
  * [Accessing Tripit](tripit_sign_in.rb)
49
49
  * Httparty included into poro class
50
50
  * Example of using `debug_output` to see headers/urls passed
51
51
  * Getting and using Cookies
52
52
  * Uses `get` requests
53
-
53
+
54
54
  * [Accessing Twitter](twitter.rb)
55
55
  * Httparty included into poro class
56
56
  * Basic Auth
57
- * Loads settings from a config file
57
+ * Loads settings from a config file
58
58
  * Uses `get` requests
59
59
  * Uses `post` requests
60
-
60
+
61
61
  * [Accessing WhoIsMyRep](whoismyrep.rb)
62
62
  * Httparty included into poro class
63
- * Uses `get` requests
63
+ * Uses `get` requests
64
64
  * Two ways to pass params to get, inline on the url or in query hash
65
65
 
66
66
  * [Rescue Json Error](rescue_json.rb)
@@ -70,3 +70,20 @@
70
70
  * Uses `get` requests
71
71
  * Uses `stream_body` mode
72
72
  * Download file without using the memory
73
+
74
+ * [Microsoft graph](microsoft_graph.rb)
75
+ * Basic Auth
76
+ * Uses `post` requests
77
+ * Uses multipart
78
+
79
+ * [Multipart](multipart.rb)
80
+ * Multipart data upload _(with and without file)_
81
+
82
+ * [Uploading File](body_stream.rb)
83
+ * Uses `body_stream` to upload file
84
+
85
+ * [Accessing x509 Peer Certificate](peer_cert.rb)
86
+ * Provides access to the server's TLS certificate
87
+
88
+ * [Accessing IDNs](idn.rb)
89
+ * Uses a `get` request with an International domain names, which are Urls with emojis and non-ASCII characters such as accented letters.
data/examples/aaws.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'active_support'
3
+ require 'active_support/core_ext/hash'
4
+ require 'active_support/core_ext/string'
3
5
 
4
6
  dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
5
7
  require File.join(dir, 'httparty')
@@ -13,14 +15,16 @@ module AAWS
13
15
  default_params Service: 'AWSECommerceService', Operation: 'ItemSearch', SearchIndex: 'Books'
14
16
 
15
17
  def initialize(key)
16
- self.class.default_params AWSAccessKeyId: key
18
+ @auth = { AWSAccessKeyId: key }
17
19
  end
18
20
 
19
21
  def search(options = {})
20
22
  raise ArgumentError, 'You must search for something' if options[:query].blank?
21
23
 
22
24
  # amazon uses nasty camelized query params
23
- options[:query] = options[:query].inject({}) { |h, q| h[q[0].to_s.camelize] = q[1]; h }
25
+ options[:query] = options[:query]
26
+ .reverse_merge(@auth)
27
+ .transform_keys { |k| k.to_s.camelize }
24
28
 
25
29
  # make a request and return the items (NOTE: this doesn't handle errors at this point)
26
30
  self.class.get('/onca/xml', options)['ItemSearchResponse']['Items']
@@ -0,0 +1,14 @@
1
+ # To upload file to a server use :body_stream
2
+
3
+ HTTParty.put(
4
+ 'http://localhost:3000/train',
5
+ body_stream: File.open('sample_configs/config_train_server_md.yml', 'r')
6
+ )
7
+
8
+
9
+ # Actually, it works with any IO object
10
+
11
+ HTTParty.put(
12
+ 'http://localhost:3000/train',
13
+ body_stream: StringIO.new('foo')
14
+ )
data/examples/idn.rb ADDED
@@ -0,0 +1,10 @@
1
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require File.join(dir, 'httparty')
3
+ require 'pp'
4
+
5
+ class Idn
6
+ include HTTParty
7
+ uri_adapter Addressable::URI
8
+ end
9
+
10
+ pp Idn.get("https://i❤️.ws/emojidomain/💎?format=json")
@@ -0,0 +1,52 @@
1
+ require 'httparty'
2
+
3
+ class MicrosoftGraph
4
+ MS_BASE_URL = "https://login.microsoftonline.com".freeze
5
+ TOKEN_REQUEST_PATH = "oauth2/v2.0/token".freeze
6
+
7
+ def initialize(tenant_id)
8
+ @tenant_id = tenant_id
9
+ end
10
+
11
+ # Make a request to the Microsoft Graph API, for instance https://graph.microsoft.com/v1.0/users
12
+ def request(url)
13
+ return false unless (token = bearer_token)
14
+
15
+ response = HTTParty.get(
16
+ url,
17
+ headers: {
18
+ Authorization: "Bearer #{token}"
19
+ }
20
+ )
21
+
22
+ return false unless response.code == 200
23
+
24
+ return JSON.parse(response.body)
25
+ end
26
+
27
+ private
28
+
29
+ # A post to the Microsoft Graph to get a bearer token for the specified tenant. In this example
30
+ # our Rails application has already been given permission to request these tokens by the admin of
31
+ # the specified tenant_id.
32
+ #
33
+ # See here for more information https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_service
34
+ #
35
+ # This request also makes use of the multipart/form-data post body.
36
+ def bearer_token
37
+ response = HTTParty.post(
38
+ "#{MS_BASE_URL}/#{@tenant_id}/#{TOKEN_REQUEST_PATH}",
39
+ multipart: true,
40
+ body: {
41
+ client_id: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_id],
42
+ client_secret: Rails.application.credentials[Rails.env.to_sym][:microsoft_client_secret],
43
+ scope: 'https://graph.microsoft.com/.default',
44
+ grant_type: 'client_credentials'
45
+ }
46
+ )
47
+
48
+ return false unless response.code == 200
49
+
50
+ JSON.parse(response.body)['access_token']
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ # If you are uploading file in params, multipart will used as content-type automatically
2
+
3
+ HTTParty.post(
4
+ 'http://localhost:3000/user',
5
+ body: {
6
+ name: 'Foo Bar',
7
+ email: 'example@email.com',
8
+ avatar: File.open('/full/path/to/avatar.jpg')
9
+ }
10
+ )
11
+
12
+
13
+ # However, you can force it yourself
14
+
15
+ HTTParty.post(
16
+ 'http://localhost:3000/user',
17
+ multipart: true,
18
+ body: {
19
+ name: 'Foo Bar',
20
+ email: 'example@email.com'
21
+ }
22
+ )
@@ -0,0 +1,9 @@
1
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require File.join(dir, 'httparty')
3
+
4
+ peer_cert = nil
5
+ HTTParty.get("https://www.example.com") do |fragment|
6
+ peer_cert ||= fragment.connection.peer_cert
7
+ end
8
+
9
+ puts "The server's certificate expires #{peer_cert.not_after}"
@@ -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
- print "."
13
- file.write(fragment)
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,15 @@ 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 = "http://jnunemaker.github.com/httparty"
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.0.0'
16
+ s.required_ruby_version = '>= 2.7.0'
17
17
 
18
+ s.add_dependency 'csv'
18
19
  s.add_dependency 'multi_xml', ">= 0.5.2"
20
+ s.add_dependency 'mini_mime', ">= 1.0.0"
19
21
 
20
22
  # If this line is removed, all hard partying will cease.
21
23
  s.post_install_message = "When you HTTParty, you must party hard!"
@@ -24,7 +26,6 @@ Gem::Specification.new do |s|
24
26
  test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
27
 
26
28
  s.files = all_files - test_files
27
- s.test_files = test_files
28
29
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
29
30
  s.require_paths = ["lib"]
30
31
  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
- # HTTPParty::ConnectionAdapter will make it easier. Just override
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(host, port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
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,28 @@ module HTTParty
100
116
 
101
117
  attach_ssl_certificates(http, options)
102
118
 
103
- if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
119
+ if add_timeout?(options[:timeout])
104
120
  http.open_timeout = options[:timeout]
105
121
  http.read_timeout = options[:timeout]
122
+ http.write_timeout = options[:timeout]
106
123
  end
107
124
 
108
- if options[:read_timeout] && (options[:read_timeout].is_a?(Integer) || options[:read_timeout].is_a?(Float))
125
+ if add_timeout?(options[:read_timeout])
109
126
  http.read_timeout = options[:read_timeout]
110
127
  end
111
128
 
112
- if options[:open_timeout] && (options[:open_timeout].is_a?(Integer) || options[:open_timeout].is_a?(Float))
129
+ if add_timeout?(options[:open_timeout])
113
130
  http.open_timeout = options[:open_timeout]
114
131
  end
115
132
 
133
+ if add_timeout?(options[:write_timeout])
134
+ http.write_timeout = options[:write_timeout]
135
+ end
136
+
137
+ if add_max_retries?(options[:max_retries])
138
+ http.max_retries = options[:max_retries]
139
+ end
140
+
116
141
  if options[:debug_output]
117
142
  http.set_debug_output(options[:debug_output])
118
143
  end
@@ -125,19 +150,11 @@ module HTTParty
125
150
  #
126
151
  # @see https://bugs.ruby-lang.org/issues/6617
127
152
  if options[:local_host]
128
- if RUBY_VERSION >= "2.0.0"
129
- http.local_host = options[:local_host]
130
- else
131
- Kernel.warn("Warning: option :local_host requires Ruby version 2.0 or later")
132
- end
153
+ http.local_host = options[:local_host]
133
154
  end
134
155
 
135
156
  if options[:local_port]
136
- if RUBY_VERSION >= "2.0.0"
137
- http.local_port = options[:local_port]
138
- else
139
- Kernel.warn("Warning: option :local_port requires Ruby version 2.0 or later")
140
- end
157
+ http.local_port = options[:local_port]
141
158
  end
142
159
 
143
160
  http
@@ -145,6 +162,14 @@ module HTTParty
145
162
 
146
163
  private
147
164
 
165
+ def add_timeout?(timeout)
166
+ timeout && (timeout.is_a?(Integer) || timeout.is_a?(Float))
167
+ end
168
+
169
+ def add_max_retries?(max_retries)
170
+ max_retries && max_retries.is_a?(Integer) && max_retries >= 0
171
+ end
172
+
148
173
  def clean_host(host)
149
174
  strip_ipv6_brackets(host)
150
175
  end
@@ -169,8 +194,7 @@ module HTTParty
169
194
  http.cert_store = options[:cert_store]
170
195
  else
171
196
  # Use the default cert store by default, i.e. system ca certs
172
- http.cert_store = OpenSSL::X509::Store.new
173
- http.cert_store.set_default_paths
197
+ http.cert_store = self.class.default_cert_store
174
198
  end
175
199
  else
176
200
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
@@ -180,7 +204,7 @@ module HTTParty
180
204
  # Note: options[:pem] must contain the content of a PEM file having the private key appended
181
205
  if options[:pem]
182
206
  http.cert = OpenSSL::X509::Certificate.new(options[:pem])
183
- http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
207
+ http.key = OpenSSL::PKey.read(options[:pem], options[:pem_password])
184
208
  http.verify_mode = verify_ssl_certificate? ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
185
209
  end
186
210
 
@@ -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(value)
5
- case value
6
+ def add_cookies(data)
7
+ case data
6
8
  when Hash
7
- merge!(value)
9
+ merge!(data)
8
10
  when String
9
- value.split('; ').each do |cookie|
10
- array = cookie.split('=', 2)
11
- self[array[0].to_sym] = array[1]
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
- select { |k, v| !CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
21
+ select { |k, v| !CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join('; ')
20
22
  end
21
23
  end