httparty 0.16.2 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
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