httpx 0.10.0 → 0.11.2

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/doc/release_notes/0_10_1.md +37 -0
  4. data/doc/release_notes/0_10_2.md +5 -0
  5. data/doc/release_notes/0_11_0.md +76 -0
  6. data/doc/release_notes/0_11_1.md +1 -0
  7. data/doc/release_notes/0_11_2.md +5 -0
  8. data/lib/httpx/adapters/datadog.rb +205 -0
  9. data/lib/httpx/adapters/faraday.rb +0 -2
  10. data/lib/httpx/adapters/webmock.rb +123 -0
  11. data/lib/httpx/chainable.rb +8 -7
  12. data/lib/httpx/connection.rb +4 -15
  13. data/lib/httpx/connection/http1.rb +14 -1
  14. data/lib/httpx/connection/http2.rb +15 -16
  15. data/lib/httpx/domain_name.rb +1 -3
  16. data/lib/httpx/errors.rb +3 -1
  17. data/lib/httpx/headers.rb +1 -0
  18. data/lib/httpx/io/ssl.rb +4 -8
  19. data/lib/httpx/io/udp.rb +4 -3
  20. data/lib/httpx/plugins/compression.rb +1 -1
  21. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +1 -1
  22. data/lib/httpx/plugins/expect.rb +33 -8
  23. data/lib/httpx/plugins/multipart.rb +42 -23
  24. data/lib/httpx/plugins/multipart/encoder.rb +115 -0
  25. data/lib/httpx/plugins/multipart/mime_type_detector.rb +64 -0
  26. data/lib/httpx/plugins/multipart/part.rb +34 -0
  27. data/lib/httpx/plugins/proxy.rb +16 -2
  28. data/lib/httpx/plugins/proxy/socks4.rb +14 -16
  29. data/lib/httpx/plugins/proxy/socks5.rb +3 -2
  30. data/lib/httpx/plugins/push_promise.rb +2 -2
  31. data/lib/httpx/pool.rb +8 -14
  32. data/lib/httpx/request.rb +22 -12
  33. data/lib/httpx/resolver.rb +7 -6
  34. data/lib/httpx/resolver/https.rb +18 -23
  35. data/lib/httpx/resolver/native.rb +22 -19
  36. data/lib/httpx/resolver/resolver_mixin.rb +4 -2
  37. data/lib/httpx/resolver/system.rb +3 -3
  38. data/lib/httpx/selector.rb +9 -13
  39. data/lib/httpx/session.rb +24 -21
  40. data/lib/httpx/transcoder.rb +20 -0
  41. data/lib/httpx/transcoder/form.rb +9 -1
  42. data/lib/httpx/version.rb +1 -1
  43. data/sig/connection.rbs +84 -1
  44. data/sig/connection/http1.rbs +66 -0
  45. data/sig/connection/http2.rbs +73 -0
  46. data/sig/headers.rbs +3 -0
  47. data/sig/httpx.rbs +1 -0
  48. data/sig/options.rbs +3 -3
  49. data/sig/plugins/basic_authentication.rbs +1 -1
  50. data/sig/plugins/compression.rbs +1 -1
  51. data/sig/plugins/compression/brotli.rbs +1 -1
  52. data/sig/plugins/compression/deflate.rbs +1 -1
  53. data/sig/plugins/compression/gzip.rbs +1 -1
  54. data/sig/plugins/h2c.rbs +1 -1
  55. data/sig/plugins/multipart.rbs +29 -4
  56. data/sig/plugins/persistent.rbs +1 -1
  57. data/sig/plugins/proxy.rbs +2 -2
  58. data/sig/plugins/proxy/ssh.rbs +1 -1
  59. data/sig/plugins/rate_limiter.rbs +1 -1
  60. data/sig/pool.rbs +36 -2
  61. data/sig/request.rbs +2 -2
  62. data/sig/resolver.rbs +26 -0
  63. data/sig/resolver/https.rbs +51 -0
  64. data/sig/resolver/native.rbs +60 -0
  65. data/sig/resolver/resolver_mixin.rbs +27 -0
  66. data/sig/resolver/system.rbs +17 -0
  67. data/sig/response.rbs +2 -2
  68. data/sig/selector.rbs +20 -0
  69. data/sig/session.rbs +3 -3
  70. data/sig/transcoder.rbs +4 -2
  71. data/sig/transcoder/body.rbs +2 -0
  72. data/sig/transcoder/form.rbs +8 -2
  73. data/sig/transcoder/json.rbs +3 -1
  74. metadata +47 -48
  75. data/lib/httpx/resolver/options.rb +0 -25
  76. data/sig/missing.rbs +0 -12
  77. data/sig/test.rbs +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e3727a5374ad3c2d6d7c5dbfd61040e3aa3ea6c74aa6ad12d662a5bd0b9a7af
4
- data.tar.gz: 7b1a2aa15418b03fc276b81cfaf0565bbce59dd531c2bfc10f19ddc60506213b
3
+ metadata.gz: 3455bd74f9d6c0cf65936bae67bf74b4d1d5fde21d254446cb4a4615a65b9bb9
4
+ data.tar.gz: a8f4c6d743667725fcfb3f43c42ef0efbcdc9772f84ecd45f558d2d4c94c3345
5
5
  SHA512:
6
- metadata.gz: cda3ab9929396bdbad4ff38472dea4d7af34946e34cf995228ad5d52d05e37664b53f66e0a0ac8dd10193e55de52b4f7575f203bea8202b5474f0c3cca450d24
7
- data.tar.gz: 025a985fc7c05abbb1bd66d23356d5165f7536d6565464e0729c618da624fc9f428e1046bba7c09dc5d7c23ada0587f73fb23e42128c96847857bcc1ccd6d408
6
+ metadata.gz: 835072956f05fcb3a42b3e128d64e5753409aa32e875426dda24d8ddbbffdd0fc6585a6d48abd48cd470962f0a57492c4b74afe529c4ab5c750d3039fdbe2b3d
7
+ data.tar.gz: 8c8be2d19b36b4ad37f00a9f8ea9378e2f60e6e65e4f9513e84b24139480f529650e74c2a3c36a200d0e318ce86c2b15be37a2522032225ba0a9055cbac4534b
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # HTTPX: A Ruby HTTP library for tomorrow... and beyond!
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/httpx.svg)](http://rubygems.org/gems/httpx)
4
- [![pipeline status](https://gitlab.com/honeyryderchuck/httpx/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/httpx/commits/master)
4
+ [![pipeline status](https://gitlab.com/honeyryderchuck/httpx/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/httpx/pipelines?page=1&scope=all&ref=master)
5
5
  [![coverage report](https://gitlab.com/honeyryderchuck/httpx/badges/master/coverage.svg?job=coverage)](https://honeyryderchuck.gitlab.io/httpx/coverage/#_AllFiles)
6
6
 
7
7
  HTTPX is an HTTP client library for the Ruby programming language.
@@ -85,7 +85,15 @@ However if the server supports HTTP/1.1, it will try to use HTTP pipelining, fal
85
85
 
86
86
  ### Clean API
87
87
 
88
- `HTTPX` acknowledges the ease-of-use of the [http gem](https://github.com/httprb/http) API (itself inspired by python [requests](http://docs.python-requests.org/en/latest/) library). It therefore aims at reusing the same facade, extending it for the use cases which the http gem doesn't support.
88
+ `httpx` builds all functions around the `HTTPX` module, so that all calls can compose of each other. Here are a few examples:
89
+
90
+ ```ruby
91
+ response = HTTPX.get("https://www.google.com")
92
+ response = HTTPX.post("https://www.nghttp2.org/httpbin/post", params: {name: "John", age: "22"})
93
+ response = HTTPX.plugin(:basic_authentication)
94
+ .basic_authentication("user", "pass")
95
+ .get("https://www.google.com")
96
+ ```
89
97
 
90
98
  ### Lightweight
91
99
 
@@ -135,7 +143,7 @@ Doesn't work with ruby 2.4.0 for Windows (see [#36](https://gitlab.com/honeyryde
135
143
 
136
144
  * Discuss your contribution in an issue
137
145
  * Fork it
138
- * Make your changes, add some test
146
+ * Make your changes, add some tests
139
147
  * Ensure all tests pass (`bundle exec rake test`)
140
148
  * Open a Merge Request (that's Pull Request in Github-ish)
141
149
  * Wait for feedback
@@ -0,0 +1,37 @@
1
+ # 0.10.1
2
+
3
+ ## Improvements
4
+
5
+ ### URL-encoded nested params
6
+
7
+ url encoder now supports nested params, which is a standard of rack-based frameworks:
8
+
9
+ ```ruby
10
+ HTTPX.post("https://httpbin.org/post", form: { a: { b: 1 }, c: [2, 3] })
11
+ # a[b]=1&c[]=2&c[]=3
12
+ ```
13
+
14
+ This encoding scheme is now the standard for URL-encoded request bodies, query params, and `:multipart` plugin requests.
15
+
16
+ ### Socks4 IPv6 addresses
17
+
18
+ HTTPX supports IPv6 Socks4 proxies now. This support is restricted to rubies where `IPAddr#hton` is implemented though, so you are encouraged to upgrade.
19
+
20
+ ## More verbose HTTP Errors
21
+
22
+ `HTTPX::Response#raise_for_status` was raising exceptions for client/server HTTP errors codes (4xx/5xx). However, only the status code was part of the message.
23
+
24
+ From now on, both headers and the responnse payload will also appear, so expected more verbosity, but also more meaningful information.
25
+
26
+ ## Bugfixes
27
+
28
+ * HTTP/2 and HTTP/1.1 exhausted connections now get properly migrated into a new connection;
29
+ * HTTP/2 421 responses will now correctly migrate the connection and pendign requests to HTTP/1.1 (a hanging loop was being caused);
30
+ * HTTP/2 connection failed with a GOAWAY settings timeout will now return error responses (instead of hanging indefinitely);
31
+ * Non-IP proxy name-resolving errors will now move on to the next available proxy in the list (instead of hanging indefinitely);
32
+ * Non-IP DNS resolve errors for `native` and `https` variants will now return the appropriate error response (instead of hanging indefinitely);
33
+
34
+ ## Chore
35
+
36
+ * `HTTPX.plugins` is now officially deprecated (use `HTTPX.plugin` instead);
37
+
@@ -0,0 +1,5 @@
1
+ # 0.10.2
2
+
3
+ ## Bugfixes
4
+
5
+ Support for nested params in multipart forms introduced a bug where top-level params weren't being spread out correctly in the request body.
@@ -0,0 +1,76 @@
1
+ # 0.11.0
2
+
3
+ ## Features
4
+
5
+ ### Webmock Adapter
6
+
7
+ `httpx` can now be integrated with `webmock`, a popular HTTP requests stubbing library.
8
+
9
+ ```ruby
10
+ # minitest
11
+ require "webmock/minitest"
12
+ require "httpx/adapters/webmock"
13
+
14
+ # in rspec
15
+ require "webmock/rspec"
16
+ require "httpx/adapters/webmock"
17
+
18
+ # and now you're free for mocking
19
+ WebMock.enable!
20
+ stub_http_request(:get, "https://www.google.com").and_return(status: 200, body: "here's google")
21
+
22
+ ```
23
+
24
+ Read more about it in the [webmock integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Webmock-Adapter).
25
+
26
+ ### Datadog Adapter
27
+
28
+ `httpx` ships with integration for [ddtrace, datadog's official tracing client](https://github.com/DataDog/dd-trace-rb). You just need to initialize it the following way:
29
+
30
+ ```ruby
31
+ require "ddtrace"
32
+ require "httpx/adapters/datadog"
33
+
34
+ Datadog.configure do |c|
35
+ c.use :httpx
36
+ end
37
+ ```
38
+
39
+ A trace will be emitted for every request, so this should be an interesting visualization if concurrent requests are sent.
40
+
41
+ Customization options and traces are similar to what [the net-http adapter provides](https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#nethttp).
42
+
43
+ Read more about it in the [datadog integration documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Datadog-Adapter).
44
+
45
+ ## Improvements
46
+
47
+ ### Own multipart request encoder
48
+
49
+ `httpx` now ships with its own multipart formdata encoder, and does not rely on `http-form_data` anymore:
50
+
51
+ ```ruby
52
+ HTTPX.plugin(:multipart).post(uri, form: {file: File.new("path/to/file")})
53
+ ```
54
+
55
+ Read more about it in the [multipart plugin documentation](https://honeyryderchuck.gitlab.io/httpx/wiki/Multipart-Uploads), including also about why this was made.
56
+
57
+ ### Expect Plugin
58
+
59
+ The `:expect` plugin now works reliably when the server does not support the `expect: 100-continue` header, i.e. it'll upload the body after a certain timeout. Building onn that, two behaviours are now implemented:
60
+
61
+ * A cache of domains which did not respond to the `expect` header is now kept, so that subsequent requests can skip the timeout and immediately upload the payload.
62
+ * If the "100 Continue" response arrives **after** the timeout expired and the body has been uploaded, the domain is removed from the cache, and subsequent requests will send the `expect` header.
63
+
64
+ ### SNI/Host options
65
+
66
+ Some extension of the API was applied in order to support custom TLS negotiation parameters. You can now pass `:hostname` under the `:ssl` options, and this will be used for the SNI part of the TLS negotiation. This is useful in scenarios where a proxy certificate doesn't apply for the host one wants to send the request to:
67
+
68
+ ```ruby
69
+ response = session.get(proxy_ip, headers: { "host" => upstream_hostname }, ssl: { hostname: sni_hostname }
70
+ ```
71
+
72
+ ## Bugfixes
73
+
74
+ A default 5 second timeout is in-place when using the DNS `:system` resolver, as it was found out that. when using the `resolv` library, the DNS query will not be retried otherwise. You can change this setting py passing `resolver_options: { timeouts: ANOTHER_TIMEOUT}`. In the future, this may become another timeout option, however.
75
+
76
+
@@ -0,0 +1 @@
1
+ 0_11_1.md
@@ -0,0 +1,5 @@
1
+ # 0.11.2
2
+
3
+ ## Bugfixes
4
+
5
+ The `:cookies` plugin wasn't able to parse `Expires` values, as it was using `Time.httpdate` to parse timestamps, which is RFC 2616-compliant, whereas cookies datetime values need to be RFC 6265-compliant.
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ddtrace/contrib/integration"
4
+ require "ddtrace/contrib/rest_client/configuration/settings"
5
+ require "ddtrace/contrib/rest_client/patcher"
6
+
7
+ module Datadog
8
+ module Contrib
9
+ module HTTPX
10
+ # HTTPX Datadog Plugin
11
+ #
12
+ # Enables tracing for httpx requests. A span will be created for each individual requests,
13
+ # and it'll trace since the moment it is fed to the connection, until the moment the response is
14
+ # fed back to the session.
15
+ #
16
+ module Plugin
17
+ class RequestTracer
18
+ SPAN_REQUEST = "httpx.request"
19
+
20
+ def initialize(request)
21
+ @request = request
22
+ end
23
+
24
+ def call
25
+ return if skip_tracing?
26
+
27
+ @request.on(:response, &method(:finish))
28
+
29
+ verb = @request.verb.to_s.upcase
30
+ uri = @request.uri
31
+
32
+ @span = datadog_pin.tracer.trace(SPAN_REQUEST)
33
+ service_name = datadog_config[:split_by_domain] ? uri.host : datadog_pin.service_name
34
+
35
+ begin
36
+ @span.service = service_name
37
+ @span.span_type = Datadog::Ext::HTTP::TYPE_OUTBOUND
38
+ @span.resource = verb
39
+
40
+ Datadog::HTTPPropagator.inject!(@span.context, @request.headers) if datadog_pin.tracer.enabled && !skip_distributed_tracing?
41
+
42
+ # Add additional request specific tags to the span.
43
+
44
+ @span.set_tag(Datadog::Ext::HTTP::URL, @request.path)
45
+ @span.set_tag(Datadog::Ext::HTTP::METHOD, verb)
46
+
47
+ @span.set_tag(Datadog::Ext::NET::TARGET_HOST, uri.host)
48
+ @span.set_tag(Datadog::Ext::NET::TARGET_PORT, uri.port.to_s)
49
+
50
+ # Tag as an external peer service
51
+ @span.set_tag(Datadog::Ext::Integration::TAG_PEER_SERVICE, @span.service)
52
+
53
+ # Set analytics sample rate
54
+ if Contrib::Analytics.enabled?(datadog_config[:analytics_enabled])
55
+ Contrib::Analytics.set_sample_rate(@span, datadog_config[:analytics_sample_rate])
56
+ end
57
+ rescue StandardError => e
58
+ Datadog.logger.error("error preparing span for http request: #{e}")
59
+ end
60
+ rescue StandardError => e
61
+ Datadog.logger.debug("Failed to start span: #{e}")
62
+ end
63
+
64
+ def finish(response)
65
+ return unless @span
66
+
67
+ if response.respond_to?(:error)
68
+ @span.set_error(response.error)
69
+ else
70
+ @span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status.to_s)
71
+
72
+ @span.set_error(::HTTPX::HTTPError.new(response)) if response.status >= 400 && response.status <= 599
73
+ end
74
+
75
+ @span.finish
76
+ end
77
+
78
+ private
79
+
80
+ def skip_tracing?
81
+ return true if @request.headers.key?(Datadog::Ext::Transport::HTTP::HEADER_META_TRACER_VERSION)
82
+
83
+ return false unless @datadog_pin
84
+
85
+ span = @datadog_pin.tracer.active_span
86
+
87
+ return true if span && (span.name == SPAN_REQUEST)
88
+
89
+ false
90
+ end
91
+
92
+ def skip_distributed_tracing?
93
+ return !datadog_pin.config[:distributed_tracing] if datadog_pin.config && datadog_pin.config.key?(:distributed_tracing)
94
+
95
+ !Datadog.configuration[:httpx][:distributed_tracing]
96
+ end
97
+
98
+ def datadog_pin
99
+ @datadog_pin ||= begin
100
+ service = datadog_config[:service_name]
101
+ tracer = datadog_config[:tracer]
102
+
103
+ Datadog::Pin.new(
104
+ service,
105
+ app: "httpx",
106
+ app_type: Datadog::Ext::AppTypes::WEB,
107
+ tracer: -> { tracer }
108
+ )
109
+ end
110
+ end
111
+
112
+ def datadog_config
113
+ @datadog_config ||= Datadog.configuration[:httpx, @request.uri.host]
114
+ end
115
+ end
116
+
117
+ module ConnectionMethods
118
+ def send(request)
119
+ RequestTracer.new(request).call
120
+ super
121
+ end
122
+ end
123
+ end
124
+
125
+ module Configuration
126
+ # Default settings for httpx
127
+ #
128
+ class Settings < Datadog::Contrib::Configuration::Settings
129
+ option :service_name, default: "httpx"
130
+ option :distributed_tracing, default: true
131
+ option :split_by_domain, default: false
132
+
133
+ option :enabled do |o|
134
+ o.default { env_to_bool("DD_TRACE_HTTPX_ENABLED", true) }
135
+ o.lazy
136
+ end
137
+
138
+ option :analytics_enabled do |o|
139
+ o.default { env_to_bool(%w[DD_TRACE_HTTPX_ANALYTICS_ENABLED DD_HTTPX_ANALYTICS_ENABLED], false) }
140
+ o.lazy
141
+ end
142
+
143
+ option :analytics_sample_rate do |o|
144
+ o.default { env_to_float(%w[DD_TRACE_HTTPX_ANALYTICS_SAMPLE_RATE DD_HTTPX_ANALYTICS_SAMPLE_RATE], 1.0) }
145
+ o.lazy
146
+ end
147
+
148
+ option :error_handler, default: Datadog::Tracer::DEFAULT_ON_ERROR
149
+ end
150
+ end
151
+
152
+ # Patcher enables patching of 'httpx' with datadog components.
153
+ #
154
+ module Patcher
155
+ include Datadog::Contrib::Patcher
156
+
157
+ module_function
158
+
159
+ def target_version
160
+ Integration.version
161
+ end
162
+
163
+ # loads a session instannce with the datadog plugin, and replaces the
164
+ # base HTTPX::Session with the patched session class.
165
+ def patch
166
+ datadog_session = ::HTTPX.plugin(Plugin)
167
+
168
+ ::HTTPX.send(:remove_const, :Session)
169
+ ::HTTPX.send(:const_set, :Session, datadog_session.class)
170
+ end
171
+ end
172
+
173
+ # Datadog Integration for HTTPX.
174
+ #
175
+ class Integration
176
+ include Contrib::Integration
177
+
178
+ # MINIMUM_VERSION = Gem::Version.new('0.11.0')
179
+ MINIMUM_VERSION = Gem::Version.new("0.10.2")
180
+
181
+ register_as :httpx
182
+
183
+ def self.version
184
+ Gem.loaded_specs["httpx"] && Gem.loaded_specs["httpx"].version
185
+ end
186
+
187
+ def self.loaded?
188
+ defined?(::HTTPX::Request)
189
+ end
190
+
191
+ def self.compatible?
192
+ super && version >= MINIMUM_VERSION
193
+ end
194
+
195
+ def default_configuration
196
+ Configuration::Settings.new
197
+ end
198
+
199
+ def patcher
200
+ Patcher
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -65,7 +65,6 @@ module Faraday
65
65
  plugin(:compression)
66
66
  plugin(:persistent)
67
67
 
68
- # :nocov:
69
68
  module ReasonPlugin
70
69
  if RUBY_VERSION < "2.5"
71
70
  def self.load_dependencies(*)
@@ -88,7 +87,6 @@ module Faraday
88
87
  end
89
88
  end
90
89
  end
91
- # :nocov:
92
90
  plugin(ReasonPlugin)
93
91
  end
94
92
 
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebMock
4
+ module HttpLibAdapters
5
+ if RUBY_VERSION < "2.5"
6
+ require "webrick/httpstatus"
7
+ HTTP_REASONS = WEBrick::HTTPStatus::StatusMessage
8
+ else
9
+ require "net/http/status"
10
+ HTTP_REASONS = Net::HTTP::STATUS_CODES
11
+ end
12
+
13
+ #
14
+ # HTTPX plugin for webmock.
15
+ #
16
+ # Requests are "hijacked" at the session, before they're distributed to a connection.
17
+ #
18
+ module Plugin
19
+ module InstanceMethods
20
+ private
21
+
22
+ def send_requests(*requests, options)
23
+ request_signatures = requests.map do |request|
24
+ request_signature = _build_webmock_request_signature(request)
25
+ WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
26
+ request_signature
27
+ end
28
+
29
+ responses = request_signatures.map do |request_signature|
30
+ WebMock::StubRegistry.instance.response_for_request(request_signature)
31
+ end
32
+
33
+ real_requests = {}
34
+
35
+ requests.each_with_index.each_with_object([request_signatures, responses]) do |(request, idx), (sig_reqs, mock_responses)|
36
+ if (webmock_response = mock_responses[idx])
37
+ mock_responses[idx] = _build_from_webmock_response(request, webmock_response)
38
+ WebMock::CallbackRegistry.invoke_callbacks({ lib: :httpx }, sig_reqs[idx], webmock_response)
39
+ log { "mocking #{request.uri} with #{mock_responses[idx].inspect}" }
40
+ elsif WebMock.net_connect_allowed?(sig_reqs[idx].uri)
41
+ log { "performing #{request.uri}" }
42
+ real_requests[request] = idx
43
+ else
44
+ raise WebMock::NetConnectNotAllowedError, sig_reqs[idx]
45
+ end
46
+ end
47
+
48
+ unless real_requests.empty?
49
+ reqs = real_requests.keys
50
+ reqs.zip(super(*reqs, options)).each do |req, res|
51
+ idx = real_requests[req]
52
+
53
+ if WebMock::CallbackRegistry.any_callbacks?
54
+ webmock_response = _build_webmock_response(req, res)
55
+ WebMock::CallbackRegistry.invoke_callbacks(
56
+ { lib: :httpx, real_request: true }, request_signatures[idx],
57
+ webmock_response
58
+ )
59
+ end
60
+
61
+ responses[idx] = res
62
+ end
63
+ end
64
+
65
+ responses
66
+ end
67
+
68
+ def _build_webmock_request_signature(request)
69
+ uri = WebMock::Util::URI.heuristic_parse(request.uri)
70
+ uri.path = uri.normalized_path.gsub("[^:]//", "/")
71
+
72
+ WebMock::RequestSignature.new(
73
+ request.verb,
74
+ uri.to_s,
75
+ body: request.body.each.to_a.join,
76
+ headers: request.headers.to_h
77
+ )
78
+ end
79
+
80
+ def _build_webmock_response(_request, response)
81
+ webmock_response = WebMock::Response.new
82
+ webmock_response.status = [response.status, HTTP_REASONS[response.status]]
83
+ webmock_response.body = response.body.to_s
84
+ webmock_response.headers = response.headers.to_h
85
+ webmock_response
86
+ end
87
+
88
+ def _build_from_webmock_response(request, webmock_response)
89
+ return ErrorResponse.new(request, webmock_response.exception, request.options) if webmock_response.exception
90
+
91
+ response = request.options.response_class.new(request,
92
+ webmock_response.status[0],
93
+ "2.0",
94
+ webmock_response.headers)
95
+ response << webmock_response.body.dup
96
+ response
97
+ end
98
+ end
99
+ end
100
+
101
+ class HttpxAdapter < HttpLibAdapter
102
+ adapter_for :httpx
103
+
104
+ class << self
105
+ def enable!
106
+ @original_session = ::HTTPX::Session
107
+
108
+ webmock_session = ::HTTPX.plugin(Plugin)
109
+
110
+ ::HTTPX.send(:remove_const, :Session)
111
+ ::HTTPX.send(:const_set, :Session, webmock_session.class)
112
+ end
113
+
114
+ def disable!
115
+ return unless @original_session
116
+
117
+ HTTPX.send(:remove_const, :Session)
118
+ HTTPX.send(:const_set, :Session, @original_session)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end