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.
- checksums.yaml +5 -5
- data/.editorconfig +18 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +23 -0
- data/.gitignore +2 -0
- data/.rubocop_todo.yml +1 -1
- data/Changelog.md +425 -280
- data/Gemfile +7 -0
- data/Guardfile +3 -2
- data/README.md +5 -5
- 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/idn.rb +10 -0
- 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 +4 -3
- data/lib/httparty/connection_adapter.rb +44 -20
- 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 +10 -4
- 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 +62 -0
- data/lib/httparty/module_inheritable_attributes.rb +9 -9
- data/lib/httparty/net_digest_auth.rb +15 -15
- data/lib/httparty/parser.rb +12 -5
- data/lib/httparty/request/body.rb +54 -27
- data/lib/httparty/request/multipart_boundary.rb +2 -0
- data/lib/httparty/request.rb +105 -107
- data/lib/httparty/response/headers.rb +4 -2
- data/lib/httparty/response.rb +52 -9
- 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 +81 -33
- data/script/release +4 -4
- data/website/css/common.css +1 -1
- metadata +50 -107
- data/.simplecov +0 -1
- data/.travis.yml +0 -10
- 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/tiny.gif +0 -0
- 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 -498
- 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 -56
- 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 -270
- data/spec/httparty/parser_spec.rb +0 -190
- data/spec/httparty/request/body_spec.rb +0 -60
- data/spec/httparty/request_spec.rb +0 -1312
- data/spec/httparty/response_spec.rb +0 -347
- data/spec/httparty/ssl_spec.rb +0 -74
- data/spec/httparty_spec.rb +0 -896
- 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/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
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# httparty
|
2
2
|
|
3
|
+
[](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.
|
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://
|
67
|
-
*
|
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
|
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:
|
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
|
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
|
-
|
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]
|
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,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}"
|
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,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 = "
|
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.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
|
-
#
|
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,28 @@ 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
|
+
http.write_timeout = options[:timeout]
|
106
123
|
end
|
107
124
|
|
108
|
-
if
|
125
|
+
if add_timeout?(options[:read_timeout])
|
109
126
|
http.read_timeout = options[:read_timeout]
|
110
127
|
end
|
111
128
|
|
112
|
-
if
|
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
|
-
|
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
|
-
|
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 =
|
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
|
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
|
|
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
|
-
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
|