http 5.3.1 → 6.0.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 +4 -4
- data/CHANGELOG.md +241 -41
- data/LICENSE.txt +1 -1
- data/README.md +110 -13
- data/UPGRADING.md +491 -0
- data/http.gemspec +32 -29
- data/lib/http/base64.rb +11 -1
- data/lib/http/chainable/helpers.rb +62 -0
- data/lib/http/chainable/verbs.rb +136 -0
- data/lib/http/chainable.rb +232 -136
- data/lib/http/client.rb +158 -127
- data/lib/http/connection/internals.rb +141 -0
- data/lib/http/connection.rb +126 -97
- data/lib/http/content_type.rb +61 -6
- data/lib/http/errors.rb +25 -1
- data/lib/http/feature.rb +65 -5
- data/lib/http/features/auto_deflate.rb +124 -17
- data/lib/http/features/auto_inflate.rb +38 -15
- data/lib/http/features/caching/entry.rb +178 -0
- data/lib/http/features/caching/in_memory_store.rb +63 -0
- data/lib/http/features/caching.rb +216 -0
- data/lib/http/features/digest_auth.rb +234 -0
- data/lib/http/features/instrumentation.rb +97 -17
- data/lib/http/features/logging.rb +183 -5
- data/lib/http/features/normalize_uri.rb +17 -0
- data/lib/http/features/raise_error.rb +18 -3
- data/lib/http/form_data/composite_io.rb +106 -0
- data/lib/http/form_data/file.rb +95 -0
- data/lib/http/form_data/multipart/param.rb +62 -0
- data/lib/http/form_data/multipart.rb +106 -0
- data/lib/http/form_data/part.rb +52 -0
- data/lib/http/form_data/readable.rb +58 -0
- data/lib/http/form_data/urlencoded.rb +175 -0
- data/lib/http/form_data/version.rb +8 -0
- data/lib/http/form_data.rb +102 -0
- data/lib/http/headers/known.rb +3 -0
- data/lib/http/headers/normalizer.rb +17 -36
- data/lib/http/headers.rb +172 -65
- data/lib/http/mime_type/adapter.rb +24 -9
- data/lib/http/mime_type/json.rb +19 -4
- data/lib/http/mime_type.rb +21 -3
- data/lib/http/options/definitions.rb +189 -0
- data/lib/http/options.rb +172 -125
- data/lib/http/redirector.rb +80 -75
- data/lib/http/request/body.rb +87 -6
- data/lib/http/request/builder.rb +184 -0
- data/lib/http/request/proxy.rb +83 -0
- data/lib/http/request/writer.rb +76 -16
- data/lib/http/request.rb +214 -98
- data/lib/http/response/body.rb +103 -18
- data/lib/http/response/inflater.rb +35 -7
- data/lib/http/response/parser.rb +98 -4
- data/lib/http/response/status/reasons.rb +2 -4
- data/lib/http/response/status.rb +141 -31
- data/lib/http/response.rb +219 -61
- data/lib/http/retriable/delay_calculator.rb +38 -11
- data/lib/http/retriable/errors.rb +21 -0
- data/lib/http/retriable/performer.rb +82 -38
- data/lib/http/session.rb +280 -0
- data/lib/http/timeout/global.rb +147 -34
- data/lib/http/timeout/null.rb +155 -9
- data/lib/http/timeout/per_operation.rb +139 -18
- data/lib/http/uri/normalizer.rb +82 -0
- data/lib/http/uri/parsing.rb +182 -0
- data/lib/http/uri.rb +289 -124
- data/lib/http/version.rb +2 -1
- data/lib/http.rb +11 -2
- data/sig/deps.rbs +122 -0
- data/sig/http.rbs +1619 -0
- data/test/http/base64_test.rb +28 -0
- data/test/http/client_test.rb +739 -0
- data/test/http/connection_test.rb +1533 -0
- data/test/http/content_type_test.rb +190 -0
- data/test/http/errors_test.rb +28 -0
- data/test/http/feature_test.rb +49 -0
- data/test/http/features/auto_deflate_test.rb +317 -0
- data/test/http/features/auto_inflate_test.rb +213 -0
- data/test/http/features/caching_test.rb +942 -0
- data/test/http/features/digest_auth_test.rb +996 -0
- data/test/http/features/instrumentation_test.rb +246 -0
- data/test/http/features/logging_test.rb +654 -0
- data/test/http/features/normalize_uri_test.rb +41 -0
- data/test/http/features/raise_error_test.rb +77 -0
- data/test/http/form_data/composite_io_test.rb +215 -0
- data/test/http/form_data/file_test.rb +255 -0
- data/test/http/form_data/fixtures/the-http-gem.info +1 -0
- data/test/http/form_data/multipart_test.rb +303 -0
- data/test/http/form_data/part_test.rb +90 -0
- data/test/http/form_data/urlencoded_test.rb +164 -0
- data/test/http/form_data_test.rb +232 -0
- data/test/http/headers/normalizer_test.rb +93 -0
- data/test/http/headers_test.rb +888 -0
- data/test/http/mime_type/json_test.rb +39 -0
- data/test/http/mime_type_test.rb +150 -0
- data/test/http/options/base_uri_test.rb +148 -0
- data/test/http/options/body_test.rb +21 -0
- data/test/http/options/features_test.rb +38 -0
- data/test/http/options/form_test.rb +21 -0
- data/test/http/options/headers_test.rb +32 -0
- data/test/http/options/json_test.rb +21 -0
- data/test/http/options/merge_test.rb +78 -0
- data/test/http/options/new_test.rb +37 -0
- data/test/http/options/proxy_test.rb +32 -0
- data/test/http/options_test.rb +575 -0
- data/test/http/redirector_test.rb +639 -0
- data/test/http/request/body_test.rb +318 -0
- data/test/http/request/builder_test.rb +623 -0
- data/test/http/request/writer_test.rb +391 -0
- data/test/http/request_test.rb +1733 -0
- data/test/http/response/body_test.rb +292 -0
- data/test/http/response/parser_test.rb +105 -0
- data/test/http/response/status_test.rb +322 -0
- data/test/http/response_test.rb +502 -0
- data/test/http/retriable/delay_calculator_test.rb +194 -0
- data/test/http/retriable/errors_test.rb +71 -0
- data/test/http/retriable/performer_test.rb +551 -0
- data/test/http/session_test.rb +424 -0
- data/test/http/timeout/global_test.rb +239 -0
- data/test/http/timeout/null_test.rb +218 -0
- data/test/http/timeout/per_operation_test.rb +220 -0
- data/test/http/uri/normalizer_test.rb +89 -0
- data/test/http/uri_test.rb +1140 -0
- data/test/http/version_test.rb +15 -0
- data/test/http_test.rb +818 -0
- data/test/regression_tests.rb +27 -0
- data/test/support/dummy_server/encoding_routes.rb +47 -0
- data/test/support/dummy_server/routes.rb +201 -0
- data/test/support/dummy_server/servlet.rb +81 -0
- data/test/support/dummy_server.rb +200 -0
- data/{spec → test}/support/fakeio.rb +2 -2
- data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
- data/test/support/http_handling_shared/timeout_tests.rb +134 -0
- data/test/support/http_handling_shared.rb +11 -0
- data/test/support/proxy_server.rb +207 -0
- data/test/support/servers/runner.rb +67 -0
- data/{spec → test}/support/simplecov.rb +11 -2
- data/test/support/ssl_helper.rb +108 -0
- data/test/test_helper.rb +38 -0
- metadata +108 -168
- data/.github/workflows/ci.yml +0 -67
- data/.gitignore +0 -15
- data/.rspec +0 -1
- data/.rubocop/layout.yml +0 -8
- data/.rubocop/metrics.yml +0 -4
- data/.rubocop/rspec.yml +0 -9
- data/.rubocop/style.yml +0 -32
- data/.rubocop.yml +0 -11
- data/.rubocop_todo.yml +0 -219
- data/.yardopts +0 -2
- data/CHANGES_OLD.md +0 -1002
- data/Gemfile +0 -51
- data/Guardfile +0 -18
- data/Rakefile +0 -64
- data/lib/http/headers/mixin.rb +0 -34
- data/lib/http/retriable/client.rb +0 -37
- data/logo.png +0 -0
- data/spec/lib/http/client_spec.rb +0 -556
- data/spec/lib/http/connection_spec.rb +0 -88
- data/spec/lib/http/content_type_spec.rb +0 -47
- data/spec/lib/http/features/auto_deflate_spec.rb +0 -77
- data/spec/lib/http/features/auto_inflate_spec.rb +0 -86
- data/spec/lib/http/features/instrumentation_spec.rb +0 -81
- data/spec/lib/http/features/logging_spec.rb +0 -65
- data/spec/lib/http/features/raise_error_spec.rb +0 -62
- data/spec/lib/http/headers/mixin_spec.rb +0 -36
- data/spec/lib/http/headers/normalizer_spec.rb +0 -52
- data/spec/lib/http/headers_spec.rb +0 -527
- data/spec/lib/http/options/body_spec.rb +0 -15
- data/spec/lib/http/options/features_spec.rb +0 -33
- data/spec/lib/http/options/form_spec.rb +0 -15
- data/spec/lib/http/options/headers_spec.rb +0 -24
- data/spec/lib/http/options/json_spec.rb +0 -15
- data/spec/lib/http/options/merge_spec.rb +0 -68
- data/spec/lib/http/options/new_spec.rb +0 -30
- data/spec/lib/http/options/proxy_spec.rb +0 -20
- data/spec/lib/http/options_spec.rb +0 -13
- data/spec/lib/http/redirector_spec.rb +0 -530
- data/spec/lib/http/request/body_spec.rb +0 -211
- data/spec/lib/http/request/writer_spec.rb +0 -121
- data/spec/lib/http/request_spec.rb +0 -234
- data/spec/lib/http/response/body_spec.rb +0 -85
- data/spec/lib/http/response/parser_spec.rb +0 -74
- data/spec/lib/http/response/status_spec.rb +0 -253
- data/spec/lib/http/response_spec.rb +0 -262
- data/spec/lib/http/retriable/delay_calculator_spec.rb +0 -69
- data/spec/lib/http/retriable/performer_spec.rb +0 -302
- data/spec/lib/http/uri/normalizer_spec.rb +0 -95
- data/spec/lib/http/uri_spec.rb +0 -71
- data/spec/lib/http_spec.rb +0 -535
- data/spec/regression_specs.rb +0 -24
- data/spec/spec_helper.rb +0 -89
- data/spec/support/black_hole.rb +0 -13
- data/spec/support/dummy_server/servlet.rb +0 -203
- data/spec/support/dummy_server.rb +0 -44
- data/spec/support/fuubar.rb +0 -21
- data/spec/support/http_handling_shared.rb +0 -190
- data/spec/support/proxy_server.rb +0 -39
- data/spec/support/servers/config.rb +0 -11
- data/spec/support/servers/runner.rb +0 -19
- data/spec/support/ssl_helper.rb +0 -104
- /data/{spec → test}/support/capture_warning.rb +0 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTP
|
|
4
|
+
module Chainable
|
|
5
|
+
# HTTP verb shortcut methods
|
|
6
|
+
#
|
|
7
|
+
# Each method delegates to {Chainable#request} with the appropriate verb.
|
|
8
|
+
module Verbs
|
|
9
|
+
# Request a get sans response body
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# HTTP.head("http://example.com")
|
|
13
|
+
#
|
|
14
|
+
# @param [String, URI] uri URI to request
|
|
15
|
+
# @param options [Hash] request options
|
|
16
|
+
# @yieldparam response [HTTP::Response] the response
|
|
17
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
18
|
+
# @api public
|
|
19
|
+
def head(uri, **, &)
|
|
20
|
+
request(:head, uri, **, &) # steep:ignore
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Get a resource
|
|
24
|
+
#
|
|
25
|
+
# @example
|
|
26
|
+
# HTTP.get("http://example.com")
|
|
27
|
+
#
|
|
28
|
+
# @param [String, URI] uri URI to request
|
|
29
|
+
# @param options [Hash] request options
|
|
30
|
+
# @yieldparam response [HTTP::Response] the response
|
|
31
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
32
|
+
# @api public
|
|
33
|
+
def get(uri, **, &)
|
|
34
|
+
request(:get, uri, **, &) # steep:ignore
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Post to a resource
|
|
38
|
+
#
|
|
39
|
+
# @example
|
|
40
|
+
# HTTP.post("http://example.com", body: "data")
|
|
41
|
+
#
|
|
42
|
+
# @param [String, URI] uri URI to request
|
|
43
|
+
# @param options [Hash] request options
|
|
44
|
+
# @yieldparam response [HTTP::Response] the response
|
|
45
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
46
|
+
# @api public
|
|
47
|
+
def post(uri, **, &)
|
|
48
|
+
request(:post, uri, **, &) # steep:ignore
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Put to a resource
|
|
52
|
+
#
|
|
53
|
+
# @example
|
|
54
|
+
# HTTP.put("http://example.com", body: "data")
|
|
55
|
+
#
|
|
56
|
+
# @param [String, URI] uri URI to request
|
|
57
|
+
# @param options [Hash] request options
|
|
58
|
+
# @yieldparam response [HTTP::Response] the response
|
|
59
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
60
|
+
# @api public
|
|
61
|
+
def put(uri, **, &)
|
|
62
|
+
request(:put, uri, **, &) # steep:ignore
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Delete a resource
|
|
66
|
+
#
|
|
67
|
+
# @example
|
|
68
|
+
# HTTP.delete("http://example.com/resource")
|
|
69
|
+
#
|
|
70
|
+
# @param [String, URI] uri URI to request
|
|
71
|
+
# @param options [Hash] request options
|
|
72
|
+
# @yieldparam response [HTTP::Response] the response
|
|
73
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
74
|
+
# @api public
|
|
75
|
+
def delete(uri, **, &)
|
|
76
|
+
request(:delete, uri, **, &) # steep:ignore
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Echo the request back to the client
|
|
80
|
+
#
|
|
81
|
+
# @example
|
|
82
|
+
# HTTP.trace("http://example.com")
|
|
83
|
+
#
|
|
84
|
+
# @param [String, URI] uri URI to request
|
|
85
|
+
# @param options [Hash] request options
|
|
86
|
+
# @yieldparam response [HTTP::Response] the response
|
|
87
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
88
|
+
# @api public
|
|
89
|
+
def trace(uri, **, &)
|
|
90
|
+
request(:trace, uri, **, &) # steep:ignore
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Return the methods supported on the given URI
|
|
94
|
+
#
|
|
95
|
+
# @example
|
|
96
|
+
# HTTP.options("http://example.com")
|
|
97
|
+
#
|
|
98
|
+
# @param [String, URI] uri URI to request
|
|
99
|
+
# @param options [Hash] request options
|
|
100
|
+
# @yieldparam response [HTTP::Response] the response
|
|
101
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
102
|
+
# @api public
|
|
103
|
+
def options(uri, **, &)
|
|
104
|
+
request(:options, uri, **, &) # steep:ignore
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Convert to a transparent TCP/IP tunnel
|
|
108
|
+
#
|
|
109
|
+
# @example
|
|
110
|
+
# HTTP.connect("http://example.com")
|
|
111
|
+
#
|
|
112
|
+
# @param [String, URI] uri URI to request
|
|
113
|
+
# @param options [Hash] request options
|
|
114
|
+
# @yieldparam response [HTTP::Response] the response
|
|
115
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
116
|
+
# @api public
|
|
117
|
+
def connect(uri, **, &)
|
|
118
|
+
request(:connect, uri, **, &) # steep:ignore
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Apply partial modifications to a resource
|
|
122
|
+
#
|
|
123
|
+
# @example
|
|
124
|
+
# HTTP.patch("http://example.com/resource", body: "data")
|
|
125
|
+
#
|
|
126
|
+
# @param [String, URI] uri URI to request
|
|
127
|
+
# @param options [Hash] request options
|
|
128
|
+
# @yieldparam response [HTTP::Response] the response
|
|
129
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
130
|
+
# @api public
|
|
131
|
+
def patch(uri, **, &)
|
|
132
|
+
request(:patch, uri, **, &) # steep:ignore
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
data/lib/http/chainable.rb
CHANGED
|
@@ -1,126 +1,117 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "http/base64"
|
|
4
|
+
require "http/chainable/helpers"
|
|
5
|
+
require "http/chainable/verbs"
|
|
4
6
|
require "http/headers"
|
|
5
7
|
|
|
6
8
|
module HTTP
|
|
9
|
+
# HTTP verb methods and client configuration DSL
|
|
7
10
|
module Chainable
|
|
8
11
|
include HTTP::Base64
|
|
9
|
-
|
|
10
|
-
# Request a get sans response body
|
|
11
|
-
# @param uri
|
|
12
|
-
# @option options [Hash]
|
|
13
|
-
def head(uri, options = {})
|
|
14
|
-
request :head, uri, options
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Get a resource
|
|
18
|
-
# @param uri
|
|
19
|
-
# @option options [Hash]
|
|
20
|
-
def get(uri, options = {})
|
|
21
|
-
request :get, uri, options
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Post to a resource
|
|
25
|
-
# @param uri
|
|
26
|
-
# @option options [Hash]
|
|
27
|
-
def post(uri, options = {})
|
|
28
|
-
request :post, uri, options
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Put to a resource
|
|
32
|
-
# @param uri
|
|
33
|
-
# @option options [Hash]
|
|
34
|
-
def put(uri, options = {})
|
|
35
|
-
request :put, uri, options
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Delete a resource
|
|
39
|
-
# @param uri
|
|
40
|
-
# @option options [Hash]
|
|
41
|
-
def delete(uri, options = {})
|
|
42
|
-
request :delete, uri, options
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Echo the request back to the client
|
|
46
|
-
# @param uri
|
|
47
|
-
# @option options [Hash]
|
|
48
|
-
def trace(uri, options = {})
|
|
49
|
-
request :trace, uri, options
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Return the methods supported on the given URI
|
|
53
|
-
# @param uri
|
|
54
|
-
# @option options [Hash]
|
|
55
|
-
def options(uri, options = {})
|
|
56
|
-
request :options, uri, options
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Convert to a transparent TCP/IP tunnel
|
|
60
|
-
# @param uri
|
|
61
|
-
# @option options [Hash]
|
|
62
|
-
def connect(uri, options = {})
|
|
63
|
-
request :connect, uri, options
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Apply partial modifications to a resource
|
|
67
|
-
# @param uri
|
|
68
|
-
# @option options [Hash]
|
|
69
|
-
def patch(uri, options = {})
|
|
70
|
-
request :patch, uri, options
|
|
71
|
-
end
|
|
12
|
+
include Verbs
|
|
72
13
|
|
|
73
14
|
# Make an HTTP request with the given verb
|
|
15
|
+
#
|
|
16
|
+
# @example Without a block
|
|
17
|
+
# HTTP.request(:get, "http://example.com")
|
|
18
|
+
#
|
|
19
|
+
# @example With a block (auto-closes connection)
|
|
20
|
+
# HTTP.request(:get, "http://example.com") { |res| res.status }
|
|
21
|
+
#
|
|
74
22
|
# @param (see Client#request)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
23
|
+
# @yieldparam response [HTTP::Response] the response
|
|
24
|
+
# @return [HTTP::Response, Object] the response, or block return value
|
|
25
|
+
# @api public
|
|
26
|
+
def request(verb, uri, **, &block)
|
|
27
|
+
client = make_client(default_options)
|
|
28
|
+
response = client.request(verb, uri, **)
|
|
29
|
+
return response unless block
|
|
30
|
+
|
|
31
|
+
yield response
|
|
32
|
+
ensure
|
|
33
|
+
client&.close if block
|
|
83
34
|
end
|
|
84
35
|
|
|
36
|
+
# Set timeout on the request
|
|
37
|
+
#
|
|
38
|
+
# @example
|
|
39
|
+
# HTTP.timeout(10).get("http://example.com")
|
|
40
|
+
#
|
|
85
41
|
# @overload timeout(options = {})
|
|
86
42
|
# Adds per operation timeouts to the request
|
|
87
43
|
# @param [Hash] options
|
|
88
44
|
# @option options [Float] :read Read timeout
|
|
89
45
|
# @option options [Float] :write Write timeout
|
|
90
46
|
# @option options [Float] :connect Connect timeout
|
|
47
|
+
# @option options [Float] :global Global timeout (combines with per-operation)
|
|
91
48
|
# @overload timeout(global_timeout)
|
|
92
49
|
# Adds a global timeout to the full request
|
|
93
50
|
# @param [Numeric] global_timeout
|
|
51
|
+
# @return [HTTP::Session]
|
|
52
|
+
# @api public
|
|
94
53
|
def timeout(options)
|
|
95
54
|
klass, options = case options
|
|
96
|
-
when Numeric then [HTTP::Timeout::Global, {:
|
|
97
|
-
when Hash then
|
|
55
|
+
when Numeric then [HTTP::Timeout::Global, { global_timeout: options }]
|
|
56
|
+
when Hash then resolve_timeout_hash(options)
|
|
98
57
|
when :null then [HTTP::Timeout::Null, {}]
|
|
99
|
-
else raise ArgumentError,
|
|
100
|
-
|
|
58
|
+
else raise ArgumentError,
|
|
59
|
+
"Use `.timeout(:null)`, " \
|
|
60
|
+
"`.timeout(global_timeout_in_seconds)` or " \
|
|
61
|
+
"`.timeout(connect: x, write: y, read: z)`."
|
|
101
62
|
end
|
|
102
63
|
|
|
103
|
-
%i[global read write connect].each do |k|
|
|
104
|
-
next unless options.key? k
|
|
105
|
-
|
|
106
|
-
options["#{k}_timeout".to_sym] = options.delete k
|
|
107
|
-
end
|
|
108
|
-
|
|
109
64
|
branch default_options.merge(
|
|
110
|
-
:
|
|
111
|
-
:
|
|
65
|
+
timeout_class: klass,
|
|
66
|
+
timeout_options: options
|
|
112
67
|
)
|
|
113
68
|
end
|
|
114
69
|
|
|
115
|
-
#
|
|
70
|
+
# Set a base URI for resolving relative request paths
|
|
71
|
+
#
|
|
72
|
+
# The first call must use an absolute URI that includes a scheme
|
|
73
|
+
# (e.g. "https://example.com"). Once a base URI is set, subsequent chained
|
|
74
|
+
# calls may use relative paths that are resolved against the existing base.
|
|
75
|
+
#
|
|
76
|
+
# @example
|
|
77
|
+
# HTTP.base_uri("https://example.com/api/v1").get("users")
|
|
78
|
+
#
|
|
79
|
+
# @example Chaining base URIs
|
|
80
|
+
# HTTP.base_uri("https://example.com").base_uri("api/v1").get("users")
|
|
81
|
+
#
|
|
82
|
+
# @param [String, HTTP::URI] uri the base URI (absolute with scheme when
|
|
83
|
+
# no base is set; may be relative when chaining)
|
|
84
|
+
# @return [HTTP::Session]
|
|
85
|
+
# @raise [HTTP::Error] if no base URI is set and the given URI has no scheme
|
|
86
|
+
# @api public
|
|
87
|
+
def base_uri(uri)
|
|
88
|
+
branch default_options.with_base_uri(uri)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Open a persistent connection to a host
|
|
92
|
+
#
|
|
93
|
+
# Returns an {HTTP::Session} that pools persistent {HTTP::Client}
|
|
94
|
+
# instances by origin. This allows connection reuse within the same
|
|
95
|
+
# origin and transparent cross-origin redirect handling.
|
|
96
|
+
#
|
|
97
|
+
# When no host is given, the origin is derived from the configured base URI.
|
|
98
|
+
#
|
|
99
|
+
# @example
|
|
100
|
+
# HTTP.persistent("http://example.com").get("/")
|
|
101
|
+
#
|
|
102
|
+
# @example Derive host from base URI
|
|
103
|
+
# HTTP.base_uri("https://example.com/api").persistent.get("users")
|
|
104
|
+
#
|
|
105
|
+
# @overload persistent(host = nil, timeout: 5)
|
|
116
106
|
# Flags as persistent
|
|
117
|
-
# @param [String] host
|
|
107
|
+
# @param [String, nil] host connection origin (derived from base URI when nil)
|
|
118
108
|
# @option [Integer] timeout Keep alive timeout
|
|
109
|
+
# @raise [ArgumentError] if host is nil and no base URI is set
|
|
119
110
|
# @raise [Request::Error] if Host is invalid
|
|
120
|
-
# @return [HTTP::
|
|
121
|
-
# @overload persistent(host, timeout: 5, &block)
|
|
122
|
-
# Executes given block with persistent
|
|
123
|
-
#
|
|
111
|
+
# @return [HTTP::Session] Persistent session
|
|
112
|
+
# @overload persistent(host = nil, timeout: 5, &block)
|
|
113
|
+
# Executes given block with persistent session and automatically closes
|
|
114
|
+
# all connections at the end of execution.
|
|
124
115
|
#
|
|
125
116
|
# @example
|
|
126
117
|
#
|
|
@@ -140,29 +131,34 @@ module HTTP
|
|
|
140
131
|
# end
|
|
141
132
|
#
|
|
142
133
|
#
|
|
143
|
-
# @yieldparam [HTTP::
|
|
134
|
+
# @yieldparam [HTTP::Session] session Persistent session
|
|
144
135
|
# @return [Object] result of last expression in the block
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
136
|
+
# @return [HTTP::Session, Object]
|
|
137
|
+
# @api public
|
|
138
|
+
def persistent(host = nil, timeout: 5)
|
|
139
|
+
host ||= default_options.base_uri&.origin
|
|
140
|
+
raise ArgumentError, "host is required for persistent connections" unless host
|
|
141
|
+
|
|
142
|
+
options = default_options.merge(keep_alive_timeout: timeout).with_persistent(host)
|
|
143
|
+
session = branch(options)
|
|
144
|
+
return session unless block_given?
|
|
149
145
|
|
|
150
|
-
yield
|
|
146
|
+
yield session
|
|
151
147
|
ensure
|
|
152
|
-
|
|
148
|
+
session&.close if block_given?
|
|
153
149
|
end
|
|
154
150
|
|
|
155
151
|
# Make a request through an HTTP proxy
|
|
152
|
+
#
|
|
153
|
+
# @example
|
|
154
|
+
# HTTP.via("proxy.example.com", 8080).get("http://example.com")
|
|
155
|
+
#
|
|
156
156
|
# @param [Array] proxy
|
|
157
157
|
# @raise [Request::Error] if HTTP proxy is invalid
|
|
158
|
+
# @return [HTTP::Session]
|
|
159
|
+
# @api public
|
|
158
160
|
def via(*proxy)
|
|
159
|
-
proxy_hash =
|
|
160
|
-
proxy_hash[:proxy_address] = proxy[0] if proxy[0].is_a?(String)
|
|
161
|
-
proxy_hash[:proxy_port] = proxy[1] if proxy[1].is_a?(Integer)
|
|
162
|
-
proxy_hash[:proxy_username] = proxy[2] if proxy[2].is_a?(String)
|
|
163
|
-
proxy_hash[:proxy_password] = proxy[3] if proxy[3].is_a?(String)
|
|
164
|
-
proxy_hash[:proxy_headers] = proxy[2] if proxy[2].is_a?(Hash)
|
|
165
|
-
proxy_hash[:proxy_headers] = proxy[4] if proxy[4].is_a?(Hash)
|
|
161
|
+
proxy_hash = build_proxy_hash(proxy)
|
|
166
162
|
|
|
167
163
|
raise(RequestError, "invalid HTTP proxy: #{proxy_hash}") unless (2..5).cover?(proxy_hash.keys.size)
|
|
168
164
|
|
|
@@ -170,87 +166,169 @@ module HTTP
|
|
|
170
166
|
end
|
|
171
167
|
alias through via
|
|
172
168
|
|
|
173
|
-
# Make client follow redirects
|
|
174
|
-
#
|
|
175
|
-
# @
|
|
169
|
+
# Make client follow redirects
|
|
170
|
+
#
|
|
171
|
+
# @example
|
|
172
|
+
# HTTP.follow.get("http://example.com")
|
|
173
|
+
#
|
|
174
|
+
# @param [Boolean] strict (true) redirector hops policy
|
|
175
|
+
# @param [Integer] max_hops (5) maximum allowed redirect hops
|
|
176
|
+
# @param [#call, nil] on_redirect optional redirect callback
|
|
177
|
+
# @return [HTTP::Session]
|
|
176
178
|
# @see Redirector#initialize
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
# @api public
|
|
180
|
+
def follow(strict: nil, max_hops: nil, on_redirect: nil)
|
|
181
|
+
opts = { strict: strict, max_hops: max_hops, on_redirect: on_redirect }.compact
|
|
182
|
+
branch default_options.with_follow(opts)
|
|
179
183
|
end
|
|
180
184
|
|
|
181
185
|
# Make a request with the given headers
|
|
182
|
-
#
|
|
186
|
+
#
|
|
187
|
+
# @example
|
|
188
|
+
# HTTP.headers("Accept" => "text/plain").get("http://example.com")
|
|
189
|
+
#
|
|
190
|
+
# @param [Hash] headers request headers
|
|
191
|
+
# @return [HTTP::Session]
|
|
192
|
+
# @api public
|
|
183
193
|
def headers(headers)
|
|
184
194
|
branch default_options.with_headers(headers)
|
|
185
195
|
end
|
|
186
196
|
|
|
187
197
|
# Make a request with the given cookies
|
|
198
|
+
#
|
|
199
|
+
# @example
|
|
200
|
+
# HTTP.cookies(session: "abc123").get("http://example.com")
|
|
201
|
+
#
|
|
202
|
+
# @param [Hash, Array<HTTP::Cookie>] cookies cookies to set
|
|
203
|
+
# @return [HTTP::Session]
|
|
204
|
+
# @api public
|
|
188
205
|
def cookies(cookies)
|
|
189
|
-
|
|
206
|
+
value = cookies.map do |entry|
|
|
207
|
+
case entry
|
|
208
|
+
when HTTP::Cookie then entry.cookie_value
|
|
209
|
+
else
|
|
210
|
+
name, val = entry
|
|
211
|
+
HTTP::Cookie.new(name.to_s, val.to_s).cookie_value
|
|
212
|
+
end
|
|
213
|
+
end.join("; ")
|
|
214
|
+
|
|
215
|
+
headers(Headers::COOKIE => value)
|
|
190
216
|
end
|
|
191
217
|
|
|
192
218
|
# Force a specific encoding for response body
|
|
219
|
+
#
|
|
220
|
+
# @example
|
|
221
|
+
# HTTP.encoding("UTF-8").get("http://example.com")
|
|
222
|
+
#
|
|
223
|
+
# @param [String, Encoding] encoding encoding to use
|
|
224
|
+
# @return [HTTP::Session]
|
|
225
|
+
# @api public
|
|
193
226
|
def encoding(encoding)
|
|
194
227
|
branch default_options.with_encoding(encoding)
|
|
195
228
|
end
|
|
196
229
|
|
|
197
230
|
# Accept the given MIME type(s)
|
|
198
|
-
#
|
|
231
|
+
#
|
|
232
|
+
# @example
|
|
233
|
+
# HTTP.accept("application/json").get("http://example.com")
|
|
234
|
+
#
|
|
235
|
+
# @param [String, Symbol] type MIME type to accept
|
|
236
|
+
# @return [HTTP::Session]
|
|
237
|
+
# @api public
|
|
199
238
|
def accept(type)
|
|
200
239
|
headers Headers::ACCEPT => MimeType.normalize(type)
|
|
201
240
|
end
|
|
202
241
|
|
|
203
242
|
# Make a request with the given Authorization header
|
|
243
|
+
#
|
|
244
|
+
# @example
|
|
245
|
+
# HTTP.auth("Bearer token123").get("http://example.com")
|
|
246
|
+
#
|
|
204
247
|
# @param [#to_s] value Authorization header value
|
|
248
|
+
# @return [HTTP::Session]
|
|
249
|
+
# @api public
|
|
205
250
|
def auth(value)
|
|
206
251
|
headers Headers::AUTHORIZATION => value.to_s
|
|
207
252
|
end
|
|
208
253
|
|
|
209
254
|
# Make a request with the given Basic authorization header
|
|
255
|
+
#
|
|
256
|
+
# @example
|
|
257
|
+
# HTTP.basic_auth(user: "user", pass: "pass").get("http://example.com")
|
|
258
|
+
#
|
|
210
259
|
# @see http://tools.ietf.org/html/rfc2617
|
|
211
|
-
# @param [#
|
|
212
|
-
# @
|
|
213
|
-
# @
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
260
|
+
# @param [#to_s] user
|
|
261
|
+
# @param [#to_s] pass
|
|
262
|
+
# @return [HTTP::Session]
|
|
263
|
+
# @api public
|
|
264
|
+
def basic_auth(user:, pass:)
|
|
265
|
+
auth("Basic #{encode64("#{user}:#{pass}")}")
|
|
266
|
+
end
|
|
218
267
|
|
|
219
|
-
|
|
268
|
+
# Enable HTTP Digest authentication
|
|
269
|
+
#
|
|
270
|
+
# Automatically handles 401 Digest challenges by computing the digest
|
|
271
|
+
# response and retrying the request with proper credentials.
|
|
272
|
+
#
|
|
273
|
+
# @example
|
|
274
|
+
# HTTP.digest_auth(user: "admin", pass: "secret").get("http://example.com")
|
|
275
|
+
#
|
|
276
|
+
# @see https://datatracker.ietf.org/doc/html/rfc2617
|
|
277
|
+
# @param [#to_s] user
|
|
278
|
+
# @param [#to_s] pass
|
|
279
|
+
# @return [HTTP::Session]
|
|
280
|
+
# @api public
|
|
281
|
+
def digest_auth(user:, pass:)
|
|
282
|
+
use(digest_auth: { user: user, pass: pass })
|
|
220
283
|
end
|
|
221
284
|
|
|
222
285
|
# Get options for HTTP
|
|
286
|
+
#
|
|
287
|
+
# @example
|
|
288
|
+
# HTTP.default_options
|
|
289
|
+
#
|
|
223
290
|
# @return [HTTP::Options]
|
|
291
|
+
# @api public
|
|
224
292
|
def default_options
|
|
225
293
|
@default_options ||= HTTP::Options.new
|
|
226
294
|
end
|
|
227
295
|
|
|
228
296
|
# Set options for HTTP
|
|
229
|
-
#
|
|
297
|
+
#
|
|
298
|
+
# @example
|
|
299
|
+
# HTTP.default_options = { response: :object }
|
|
300
|
+
#
|
|
301
|
+
# @param [Hash, HTTP::Options] opts options to set
|
|
230
302
|
# @return [HTTP::Options]
|
|
303
|
+
# @api public
|
|
231
304
|
def default_options=(opts)
|
|
232
305
|
@default_options = HTTP::Options.new(opts)
|
|
233
306
|
end
|
|
234
307
|
|
|
235
308
|
# Set TCP_NODELAY on the socket
|
|
309
|
+
#
|
|
310
|
+
# @example
|
|
311
|
+
# HTTP.nodelay.get("http://example.com")
|
|
312
|
+
#
|
|
313
|
+
# @return [HTTP::Session]
|
|
314
|
+
# @api public
|
|
236
315
|
def nodelay
|
|
237
316
|
branch default_options.with_nodelay(true)
|
|
238
317
|
end
|
|
239
318
|
|
|
240
|
-
#
|
|
241
|
-
#
|
|
242
|
-
#
|
|
243
|
-
#
|
|
244
|
-
#
|
|
245
|
-
#
|
|
246
|
-
#
|
|
247
|
-
# @
|
|
319
|
+
# Enable one or more features
|
|
320
|
+
#
|
|
321
|
+
# @example
|
|
322
|
+
# HTTP.use(:auto_inflate).get("http://example.com")
|
|
323
|
+
#
|
|
324
|
+
# @param [Array<Symbol, Hash>] features features to enable
|
|
325
|
+
# @return [HTTP::Session]
|
|
326
|
+
# @api public
|
|
248
327
|
def use(*features)
|
|
249
328
|
branch default_options.with_features(features)
|
|
250
329
|
end
|
|
251
330
|
|
|
252
|
-
#
|
|
253
|
-
# due to some socket errors or response status is `5xx`.
|
|
331
|
+
# Return a retriable session that retries on failure
|
|
254
332
|
#
|
|
255
333
|
# @example Usage
|
|
256
334
|
#
|
|
@@ -258,23 +336,41 @@ module HTTP
|
|
|
258
336
|
# HTTP.retriable.get(url)
|
|
259
337
|
#
|
|
260
338
|
# # Retry max 3 times with randomly growing delay between retries
|
|
261
|
-
# HTTP.retriable(
|
|
339
|
+
# HTTP.retriable(tries: 3).get(url)
|
|
262
340
|
#
|
|
263
341
|
# # Retry max 3 times with 1 sec delay between retries
|
|
264
|
-
# HTTP.retriable(
|
|
342
|
+
# HTTP.retriable(tries: 3, delay: proc { 1 }).get(url)
|
|
265
343
|
#
|
|
266
344
|
# # Retry max 3 times with geometrically progressed delay between retries
|
|
267
|
-
# HTTP.retriable(
|
|
345
|
+
# HTTP.retriable(tries: 3, delay: proc { |i| 1 + i*i }).get(url)
|
|
268
346
|
#
|
|
269
347
|
# @param (see Performer#initialize)
|
|
270
|
-
|
|
271
|
-
|
|
348
|
+
# @return [HTTP::Session]
|
|
349
|
+
# @api public
|
|
350
|
+
def retriable(tries: nil, delay: nil, exceptions: nil, retry_statuses: nil,
|
|
351
|
+
on_retry: nil, max_delay: nil, should_retry: nil)
|
|
352
|
+
opts = { tries: tries, delay: delay, exceptions: exceptions, retry_statuses: retry_statuses,
|
|
353
|
+
on_retry: on_retry, max_delay: max_delay, should_retry: should_retry }.compact
|
|
354
|
+
branch default_options.with_retriable(opts.empty? || opts)
|
|
272
355
|
end
|
|
273
356
|
|
|
274
357
|
private
|
|
275
358
|
|
|
276
|
-
#
|
|
359
|
+
# Create a new session with the given options
|
|
360
|
+
#
|
|
361
|
+
# @param [HTTP::Options] options options for the session
|
|
362
|
+
# @return [HTTP::Session]
|
|
363
|
+
# @api private
|
|
277
364
|
def branch(options)
|
|
365
|
+
HTTP::Session.new(options)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# Create a new client for executing a request
|
|
369
|
+
#
|
|
370
|
+
# @param [HTTP::Options] options options for the client
|
|
371
|
+
# @return [HTTP::Client]
|
|
372
|
+
# @api private
|
|
373
|
+
def make_client(options)
|
|
278
374
|
HTTP::Client.new(options)
|
|
279
375
|
end
|
|
280
376
|
end
|